题目描述
给你两个单词 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')
算法1
(动态规划) $O(mn)$
状态表示:f[i][j]表示word1前i个字符与word2前j个字符的编辑距离。
状态转移:
1.如果word1的第i个字符与word2的第j个字符相等,那么编辑距离不会增加,仍取决于word1的前i - 1个字符与word2的前j - 1个字符,即$f[i][j] = f[i - 1][j - 1]$;
2.如果word1的第i个字符与word2的第j个字符不相等,有三种选择,修改、删除、插入:
修改:
也就是在word1的前i - 1个字符与word2的前j - 1个字符的编辑距离上又加了1,有$f[i][j] = f[i - 1][j - 1] + 1$;
删除:删除word1的第i个字符,那么编辑距离为word1的前i - 1个字符与word2的前j个字符的编辑距离上加了1,有$f[i][j] = f[i - 1][j] + 1$,
同理删除word2的第j个字符,那么编辑距离为word1的前i个字符与word2的前j - 1个字符的编辑距离上又加了1,有$f[i][j] = f[i][j - 1] + 1$;
增加:在计算编辑距离时,增加操作和删除操作其实是完全一样的,例如”exection”与” execution”,可以给”exection”加个’u’,也可以给”execution”减去个’u’,所以转移方程跟删除完全一样。
从增加的角度去理解状态方程,在word2增加一个字符前,编辑距离为$f[i][j-1]$,增加字符后,距离加1,故有$f[i][j] = f[i][j - 1] + 1$,相似的有另一种情况$f[i][j] = f[i - 1][j] + 1$。
时间复杂度
状态数位m $\times$ n个,每一次转移为$O(1)$,故总时间复杂度为$O(mn)$。
参考文献
C++ 代码
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
int f[m + 1][n + 1]; memset(f, 0, sizeof f);
for (int j = 0; j <= n; ++j) f[0][j] = j;
for (int i = 0; i <= m; ++i) f[i][0] = i;
for (int i = 1; i <= m; ++i){
for (int j = 1; j <= n; ++j){
if (word1[i - 1] == word2[j - 1]){
f[i][j] = f[i - 1][j - 1];
}
else {
f[i][j] = 1 + min(f[i - 1][j - 1], min(f[i - 1][j], f[i][j - 1]));
}
}
}
return f[m][n];
}
};
算法2
(动态规划 滚动数组优化版本) $O(mn)$
算法同上,只是实现上使用滚动数组优化了空间。
时间复杂度
时间复杂度同算法1,空间复杂度是$O(min(m, n))$
参考文献
C++ 代码
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
if (m < n) return minDistance (word2, word1);
int f[n + 1]; memset(f, 0, sizeof f);
for (int j = 0; j <= n; ++j) f[j] = j;
for (int i = 1; i <= m; ++i){
int pre = i - 1; f[0] = i;
for (int j = 1; j <= n; ++j){
int tmp = f[j];
if (word1[i - 1] == word2[j - 1]) f[j] = pre;
else f[j] = 1 + min(pre, min(f[j], f[j - 1]));
pre = tmp;
}
}
return f[n];
}
};