题目
给你两个单词 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 <= 500
word1
和word2
由小写英文字母组成
思路(好吧确实想不出来,这是我想了一天在纠结的问题集)
- 每一次比较后我们可以采取的策略有四种:插入、删除、替换、跳过。
- 因为从一个字符串变成另一个字符串,如果两者长度不一样,那么中间的差值必然会变成插入或者删除的操作数,我们是否可以先利用某种方法先转换成长度一致的两个字符串,再只考虑替换的问题呢?
- 第二,删除、替换、插入、跳过的状态变化都不一样,有没有什么办法降维或者变换使得状态变化趋于一致?
代码实现
复杂度分析
官方题解
- 看官解吧,我感觉官解都得理解一会儿才行。
- 其实最重要的就是理解每个状态该怎么定义,我把每个状态看成了单个字符的比较,没有考虑到比较到当前字符时前驱节点已经是局部最优的情况,所以后续的状态转移就非常的难受。
- 第二就是这里的局部最优的叠加就是全局最优,我一直没办法证明,当时脑子其实一动想用归纳证明一下的,但是又倔强说这玩意儿就是逆天而行,不顺着推怎么锻炼思维,然后就整死自己了qwq。
思路
- 首先是降维问题,如果把字符串A的删除操作替换为B的插入操作,那么操作类型就会直接减少。
- 这里面还有一个很重要的点,就是无论word1[i]和word2[j]是两堆什么样的字符串,如果前序已经完成变换了,那么如果i和j不一样,那么他们必然要发生一次操作。
- 然后就是状态的表示。
- 首先引入一个概念(其实就是题目的解释)——编辑距离——一个字符串转换成为另外一个字符串所需要的最小操作数。
- dp[i][j]定义A的前i个字符(即前i-1位)转换成B的前j个(前j-1位)字符所需要的编辑距离。
- 那么到达dp[i][j]可能有三种情况:
- dp[i-1][j]已经匹配,那么只要删除第i位即可——删除;
- dp[i][j-1]已经匹配,那么只要删除第j位(等价于在A添加一个和B的第j位匹配的字符)——插入;
- dp[i-1][j-1]后,说明到达i-1,j-1位时,前i-2,j-2位的内容已经匹配好了,若A[i-1]==B[i-1]就直接保持,若不等就替换其中一个。
- 那么就可以得出状态转移方程:
- dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+int(word1[i-1]!=word2[j-1]));
- 最后需要考虑初始值和边界值:
- 当字符串为空的时候,编辑距离即是双方的长度和。
- dp[i]][0],dp[0][j]分别都为i和j。
代码复现
class Solution {
public:
int minDistance(string word1, string word2) {
int n1 = word1.length(), n2 = word2.length();
if(n1*n2 == 0) return n1+n2;
int dp[n1+1][n2+1];
for(int i = 0; i <= n1; i++) dp[i][0] = i;
for(int j = 0; j <= n2; j++) dp[0][j] = j;
for(int i = 1; i <= n1; i++) {
for(int j = 1; j <= n2; j++) {
dp[i][j] = min(dp[i-1][j]+1, min(dp[i][j-1]+1, dp[i-1][j-1]+int(word1[i-1]!=word2[j-1])));
}
}
return dp[n1][n2];
}
};
复杂度分析
- 时间复杂度:O(n1*n2)。
- 空间复杂度:O(n1*n2)。
进阶:
- 因为每次状态更新都只涉及上一层的结果,所以其实可以降维成一维的动态规划,只要额外把重复的j-1部分保存即可。
- 那么空间复杂度就可以降维成O(n2)。