2976.转换字符串的最小成本I

🎠 2976.转换字符串的最小成本I

给你两个下标从 0 开始的字符串 sourcetarget ,它们的长度均为 n 并且由 小写 英文字母组成。

另给你两个下标从 0 开始的字符数组 originalchanged ,以及一个整数数组 cost ,其中 cost[i] 代表将字符 original[i] 更改为字符 changed[i] 的成本。

你从字符串 source 开始。在一次操作中,如果 存在 任意 下标 j 满足 cost[j] == zoriginal[j] == x 以及 changed[j] == y 。你就可以选择字符串中的一个字符 x 并以 z 的成本将其更改为字符 y

返回将字符串 source 转换为字符串 target 所需的 最小 成本。如果不可能完成转换,则返回 -1

思路

基本思想

题目中说会给出一部分的字符之间的转换成本,根据这些字符的转换成本得到将source转换成target的最小成本,针对source和target的每个对应位置上的字符来说,都是求这两个字符之间的最小距离,因为字符之间转换的关系可以用一个图表示出来,图中的边就是转换成本,针对每一个要从source转换到target的字符,求出其最短路径即可,这就是图的最短路径问题

首先要建立一个邻接矩阵,并更新出当前字符的一步转换成本,也就是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int[][] dist=new int[26][26];
for(int i=0;i<26;++i){
    Arrays.fill(dist[i],Integer.MAX_VALUE/2);
    dist[i][i]=0;
}
//统计一步变化距离
for(int i=0;i<original.length;++i){ 
    int begin=original[i]-'a';
    int end=changed[i]-'a';
    dist[begin][end]=Math.min(dist[begin][end],cost[i]);
}

然后利用弗洛伊德算法求出任意两点之间的最短距离,之后遍历source和target的对应位置,需要转换就从更新完毕的邻接矩阵dist中找到最短距离即可

弗洛伊德更新代码为:

1
2
3
4
5
6
7
for(int k=0;k<26;++k){
    for(int i=0;i<26;++i){
        for(int j=0;j<26;++j){
            dist[i][j]=Math.min(dist[i][j],dist[i][k]+dist[k][j]);
        }
    }
}

需要注意几个点:

  1. 初始化时要注意到自身的成本为0
  2. 到不可达的节点成本为Integer.MAX_VALUE/2,不是Integer.MAX_VALUE,也不是1000001,使用Integer.MAX_VALUE会造成弗洛伊德算法更新时溢出,使用1000001时会造成如果当前cost[i]刚好累加到了1000001,那么会认为这两个顶点不可达
  3. 使用字符一步转换的矩阵cost构造出邻接矩阵之后,才开始执行弗洛伊德算法

执行流程

  1. 初始化邻接矩阵,不可达节点间的成本为Integer.MAX_VALUE/2,到自身的成本为0
  2. 根据成本转换矩阵构造两点之间的一步成本
  3. 根据弗洛伊德算法更新两点之间的最短距离
  4. 计算source到target每个字符的转换成本并累加
  5. 统计的途中有两个字符之间无法转换(不可达),此时返回-1
  6. 统计完成返回最终的结果

代码

根据以上分析,得出以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
    //全是细节
    //看这些字符转换的最短路径(图的最短路径问题)
    public long minimumCost(String source, String target, char[] original, char[] changed, int[] cost) {
        //更新初始邻接矩阵
        int[][] dist=new int[26][26];
        for(int i=0;i<26;++i){
            Arrays.fill(dist[i],Integer.MAX_VALUE/2);
            dist[i][i]=0;
        }
        //统计一步变化距离
        for(int i=0;i<original.length;++i){ 
            int begin=original[i]-'a';
            int end=changed[i]-'a';
            dist[begin][end]=Math.min(dist[begin][end],cost[i]);
        }

        //弗洛伊德算法求出每个字符到其余字符的最短变化距离
        for(int k=0;k<26;++k){
            for(int i=0;i<26;++i){
                for(int j=0;j<26;++j){
                    dist[i][j]=Math.min(dist[i][j],dist[i][k]+dist[k][j]);
                }
            }
        }

        //要更新字符之间的直接变化次数
        //防止溢出
        long res=0;
        for(int i=0;i<source.length();++i){
            //当前两个元素压根无法转换
            if(dist[source.charAt(i)-'a'][target.charAt(i)-'a']==Integer.MAX_VALUE/2){
                return -1;
            }
            //可以转换,记录最短距离
            res+=dist[source.charAt(i)-'a'][target.charAt(i)-'a'];
        }
        return res;
        
    }
}

总结

主要是利用弗洛伊德求出图中任意两点的最短距离,前提是要构造好邻接矩阵,注意不可达的两点之间要使用Integer.MAX_VALUE/2表示