描述
给你两个单词 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 由小写英文字母组成
思路
这是动态规划中难度比较大的题了。从word1变到word2和word2变到word1是一样的(后者是前者的逆过程)。dp数组的定义都比较难想到,状态转移方程就更不必说了。既然要从word1变到word2,参考前面的最长公共子序列问题,用动态规划的思想来考虑,肯定是从word1的前0个字符开始,一直考虑到前n个字符(m为word1总长度),对照着word2中的前0个字符到前n个字符(n为word2的总长度),利用状态转移方程,每一次遍历都进行最优选择。
那么,dp数组的含义差不多就能想到了:dp[i][j]
就代表word1的前i个字符转换到word的前j个字符所需要的最小编辑距离。
初始值:因为从word1的前0个字符->word2的前j个字符 需要j步,所以dp[0][j] = j
;同理,从word1的前i个字符->word2的前0个字符 需要i步,dp[i][0] = i
。
状态转移方程:
dp[i][j]
取决于什么呢?(1)当word1[i - 1]
和word2[j - 1]
(字符串索引从0开始,所以是i - 1和j - 1)相等时,那么就不需要进行任何操作,什么也不做一定是当前的最优解,因为其他3种操作都会增加编辑距离。dp[i][j] = dp[i - 1][j - 1]
,即继承前i -1、j - 1个字符的最小编辑距离。(2)如果word1[i - 1]
和word2[j - 1]
不相等,那么就需要考虑题设的三种操作了:插入、删除、替换。应该进行哪种操作取决于谁呢?
- 删除:如果从word1的前i-1个字符变到word2的前j个字符需要k步,那么从word1的前i个字符变到word2的前j个字符只需要将
word1[i - 1]
删除即可。即dp[i][j] = dp[i - 1][j] + 1
- 插入:如果从word1的前i个字符变到word2的前j-1个字符需要k步,那么从word1的前i个字符变到word2的前j个字符只需要在word1后面插入
word[j - 1]
的值即可。即dp[i][j] = dp[i ][j - 1] + 1
- 替换:如果从word1的前i - 1个字符到word2的前j -1个字符需要k步,那么从word1的前i个字符变到word2的前j个字符只需要将
word[i - 1]
替换为word[j - 1]
即可。即dp[i][j] = dp[i - 1][j - 1] + 1
。
这样状态转移方程就列完了,每次都在四种操作(第一种什么也不做也算一种操作)中抉择即可。
参考:来自LeetCode下的精选评论
解答
class Solution {
public:
int minDistance(string word1, string word2) {
int len1 = word1.length();
int len2 = word2.length();
//dp数组,定义为:word1的前i个字符->word2的前j个字符 最少需要多少步
vector<vector<int> > dp(len1 + 1, vector(len2 + 1, 0));
//初始值:
//因为从word1的前0个字符->word2的前j个字符 需要j步,所以dp[0][j] = j
//同理,从word1的前i个字符->word2的前0个字符 需要i步,dp[i][0] = i
for(int i = 1;i <= len1; ++ i) dp[i][0] = i;
for(int j = 1;j <= len2; ++ j) dp[0][j] = j;
//状态转移方程
for(int i = 1;i <= len1; ++ i)
for(int j = 1;j <= len2; ++ j)
if(word1[i - 1] == word2[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = min(dp[i - 1][j],
min(dp[i][j - 1], dp[i - 1][j - 1])) + 1;
return dp[len1][len2];
}
};