给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1:
输入: word1 = "horse", word2 = "ros"
输出: 3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:
输入: word1 = "intention", word2 = "execution"
输出: 5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
提示:
0 <= word1.length, word2.length <= 500word1和word2由小写英文字母组成
设 dp[i][j] 表示的是:将 word1 的前 i 个字符,转换为 word2 的前 j 个字符,所需要的编辑距离(即最小操作数)
两种情况:字符相等与字符不相等
情况一:字符相等
word1 的第 i 个字符 word1[i - 1] 等于 word2 的第 j 个字符 word2[j - 1],那么不需要做任何编辑操作,此时 dp[i][j] = dp[i - 1][j - 1]
情况二:字符不等
考虑三种编辑操作:
替换
将 word1[i - 1] 替换为 word2[j - 1],此时最后一个字符相同,操作数加一,即 dp[i][j] = dp[i - 1][j - 1] + 1
插入
在 word1 中插入word2[j - 1],由于此时 word1 的长度变为 i + 1,那么相当于将 word1 的前 i 个字符转换为 word2 的前 j - 1 个字符后,再插入一个字符,即dp[i][j] = dp[i][j - 1] + 1
删除
从 word1 中删除 word1[i - 1],由于此时 word1 的长度变为 i - 1,那么相当于将 word1 的前 i - 1个字符转换为 word2 的前 j 个字符,即 dp[i][j] = dp[i - 1][j] + 1
最终,应该取这三种情况的最小值
边界情况
如果有一个串为空串,另一个串不为空串且长度为 k,那么只需要执行 k 次删除操作或增加操作即可
所以 dp[i][0] = i, dp[0][j] = j
java
class Solution {
public int minDistance(String word1, String word2) {
int n = word1.length();
int m = word2.length();
if(n * m == 0) {
// 如果有空串,只需执行 n 或 m 次删除或添加操作
return n + m;
}
int[][] dp = new int[n + 1][m + 1];
for(int i = 0; i < n + 1; i++) {
dp[i][0] = i;
}
for(int j = 0; j < m + 1; j++) {
dp[0][j] = j;
}
for(int i = 1; i < n + 1; i++) {
for(int j = 1; j < m + 1; j++) {
if(word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(dp[i - 1][j],
Math.min(dp[i][j - 1],dp[i - 1][j - 1])
) + 1;
}
}
}
return dp[n][m];
}
}
可以将 dp 数组简化为一个两行数组,因为dp[i][j]只和它左上、左边、上方的元素相关
再进一步,也可以只用一个一维数组
java
class Solution {
public int minDistance(String word1, String word2) {
int m = word2.length();
char[] w2 = word2.toCharArray();
int[] dp = new int[m + 1];
for(int j = 0; j < m; j++) {
// dp[0] 相当于 dp[i][0], 值为 i , 因此这里不做初始化,遍历 word1 再处理
dp[j + 1] = j + 1;
}
for(char ch : word1.toCharArray()) {
int pre = dp[0]; // 每次当 j = 0,dp[i][1] 的 左上角就是 dp[i - 1][0]
dp[0]++; // 每遍历一个 word1[i - 1], dp[i][0]都更新为 i , 这里由于是一维数组,因此值自增就表示值为 i
for(int j = 0; j < m; j++) {
int tmp = dp[j + 1];
if(ch == w2[j]) {
dp[j + 1] = pre;
} else {
// pre 表示左上角,dp[j + 1] 表示上方,dp[j] 表示左边
dp[j + 1] = Math.min(Math.min(dp[j + 1], dp[j]), pre) + 1;
}
pre = tmp;
}
}
return dp[m];
}
}