Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.
You have the following 3 operations permitted on a word:
- Insert a character
- Delete a character
- Replace a character
Example 1:
Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation:
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')
Example 2:
Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation:
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')
以下五月份写的思路不正确,下面十月份写的解析是正确的。
这题看了网上的很多资料,发现很多解释了该怎么做,但为什么这么做对,基本上没解释。状态转移方程是不复杂的,难的是状态转移方程为什么这么构造。结合知乎上网友的回答https://www.zhihu.com/question/23139644 谈谈自己的理解。
假设A字符串匹配B字符串,首先有几条性质:
1.A中插入的某个字符为了对应B中的某个字符,此后A和B中此字符都不会再改变了。
2.A中变换的某个字符为了对应B中的某个字符,此后A和B中此字符都不会再改变了。
3.A中的操作可以交换次序。
4.A中的插入等效于B中的删除。
5.A最小编辑距离到B,等效于B最小编辑距离到A
所以当A来匹配B可以分为这么几种情况:
1. A中的最后一个字符i删除,此时基于以上性质3可看做A中前面i-1个字符最小编辑距离到B,然后在删除A最后一个字符,此时状态转移方程最小编辑距离为dp[i][j]=dp[i-1][j]+1。
2.A中的最后一个字符i对应B中的最后一个字符j(不等就替换,相等就不变),此时基于以上性质2,3可知A中前面i-1个字符最小编辑距离到B中前j-1个字符,再处理最后一个字符,此时状态转移方程最小编辑距离为dp[i][j]=dp[i-1][j]+1,dp[i-1][j-1]+(word1.charAt(i-1)==word2.charAt(j-1)?0:1)
3.A中的最后一个字符i对应B中的其他字符,此时基于以上性质1,2,3可知,此时的编辑距离大于:A中前面i-1个字符最小编辑距离到B中前k-1(k≠j)个字符,再加上在A后面插入j-(k-1)个与B中对应相同的字符。基于性质4,5,等价于B中前面k-1个字符最小编辑距离到A中前i-1个字符,再加上在B中删除最后j-(k-1)个字符。再根据性质3,这些情况的最小编辑距离都大于等于 B中前面j-1个字符最小编辑距离到A中前i个字符,再删除B中最后一个字符j时的总编辑距离。此时状态转移方程最小编辑距离dp[i][j]=dp[i][j-1]+1。
上面3可以简化理解当A对应B中的其他字符k,根据上面性质,等价于B中K后面的字符要全部删除,其蕴含于删除B中最后一个元素的情况。
另外所有情况都蕴含在上面3中情况中。
此题还需要再思考的更透彻。
另一种理解根据上面的性质4,A和B最后一个字符只会出现3种情况,删除A最后一个字符,删除B最后一个字符,A和B都不删。
public int minDistance(String word1, String word2) {
int[][] dp=new int[word1.length()+1][word2.length()+1];//d[i][j]表示第i和j个字符匹配
for(int i=1;i<word1.length()+1;i++){
dp[i][0]=i;
}
for(int i=1;i<word2.length()+1;i++){
dp[0][i]=i;
}
for(int i=1;i<word1.length()+1;i++){
for(int j=1;j<word2.length()+1;j++){
dp[i][j]=Math.min(dp[i][j-1]+1,Math.min(dp[i-1][j]+1,dp[i-1][j-1]+(word1.charAt(i-1)==word2.charAt(j-1)?0:1)));
}
}
return dp[dp.length-1][dp[0].length-1];
}
===============================2018.10.13=========================================================
上面理解的不透彻,正确理解如下:
# dp[i][j]表示第i和j个字符匹配,dp[0][0]表示空字符,因为空字符也作为一种情况用于递推
# 找一个完备事件组,事件组为子问题,
# 在这之前要明确编辑距离的性质,word1每个字符可看做与word2中某个字符对应,不对应即删除,剩下的字符最小编辑 距离到另一个字符
# word1插入字符对应可看做word2中对应字符的删除
# 事件组:word1的最后一个字符是否对应word2,
# 情况1:不对应,删除;情况2:对应word2最后一个,情况3对应word2其他字符
# 情况3较为特殊,必定要在word1之后插入字符,否则就不对应,就等效在word2中删除对应字符,这种情况下word2的最 后一个字符必定删除
# 递推式:dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+word1[i]==word2[j]?0:1),
class Solution:
dp=[[0 for j in range(len(word2)+1)] for i in range(len(word1)+1)]
dp[0][0]= 0
for i in range(1,len(word1)+1):
dp[i][0]=i
for j in range(1,len(word2)+1):
dp[0][j]=j
for i in range(1,len(word1)+1):
for j in range(1,len(word2)+1):
dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+ (0 if word1[i-1]==word2[j-1] else 1))
return dp[len(dp)-1][len(dp[0])-1]