要求:
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
示例:
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
要求:
对于两个字符串,以一个为基准,另一个向它靠齐。都是先指向各自的最后一个元素,从尾至头的遍历。
-
base case:某个字符串的长度全部走完了,那么直接返回另一个字符串剩下的长度即可。
-
选择:插入(dp(i,j-1)+1);删除(dp(i-1,j)+1);替换(dp(i-1,j-1)+1)
-
返回值:dp(len(s1)-1, len(s2)-1);
-
状态转换:
- 插入时:在s1中插入一个和s2中某个元素一样的字母,那么s2就相当于是匹配成功了,前移,但是i还是在原地不动。所以是dp(i,j-1)+1。
- 删除时:直接把s1中的这个字母删除,不管是否匹配,所以i前移,j不变
- 替换时:两个都变,所以i,j都减一
-
dp状态表格:创建数组dp[s1.length+1][s2.length+1]来记录,避免不断重复计算路径。一般设计的时候都会是这样,行列都是各自长度加一,这样可以把第一行第一列作为基础值先设置一下,最后返回的时候索引是正常的,在循环中的索引一般都是从1开始。
-
三种状态的选择,哪一种是最优并不清楚,那么就选其中的最小值,三个都走一遍。
借用大佬labuladong的图,紫色部分都是初始设定时给定的值。
代码
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
int[][] dp = new int[m+1][n+1];
for (int i = 0; i < m+1; i++) {
dp[i][0] = i;
}
for (int i = 0; i < n+1; i++) {
dp[0][i] = i;
}
for (int i = 1; i < m+1; i++) {
for (int j = 1; j < n+1; j++) {
if (word1.charAt(i-1) == word.charAt(j-1)) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+1);
}
}
}
return dp[m][n];
}
public int min(int a, int b, int c) {
return Math.min(a, Math.min(b,c));
}
}