题目描述
给你两个单词 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’)
思路分析
题目中说请返回将 word1 转换成 word2 所使用的最少操作数 。
题目中规定了将word1转换成word2。
并且规定了增删改三种操作。
现在假设有两个字符串 ,A:dog 。B:doge
让A和B相同,则可以:
- A末尾添加一个e,变成doge。
- B删除末尾的e,变成dog
可以发现上述两种操作是等价的,注意这里是指操作次数等价,而不是值变化之后的结果等价。
同理,例如有 A:dg和B:de ,有两种情况让他们变得等价。
- 将A的末尾g变成e
- 将B的末尾e变成g
综上,删除和增加元素是等价的,替换元素也是等价的。
所以问题变成:
- 在A中删除一个字符
- 在B中删除一个字符 (等价于在A中插入一个字符)
- 替换A中的一个字符
老规矩,动态规划五步走;
1.确定dp数组下标含义:
dp[i][j] 表示 A字符串到下标i-1,B字符串到下标j-1 时的两个子串相等最小的编辑距离。
2.状态转移公式:
如果 A[i-1] == B[j-1] :
此时两字符串末尾项一样,不需要增删改,直接继承之前的最小编辑距离就行了。dp[i][j]=dp[i−1][j−1]
如果 A[i-1] != B[j-1] :
-
删A的一个元素,那么就是以下标 i−2为结尾的 A与j−1 为结尾的 B的最近编辑距离 再加上一个操作。最少操作次数为 dp[i−1][j]+1
-
删B的一个元素,那么就是以下标 i−1为结尾的 A与j−2 为结尾的 B的最近编辑距离 再加上一个操作。最少操作次数为 dp[i][j-1]+1
-
替换掉A的末尾元素 也就是 A[i-2],使其等于B[j-2],那么就是以下标 i−2 为结尾的 word1 与 j−2 为结尾的 word2 的最近编辑距离 即 dp[i-1][j-1] 再加上一个替换元素的操作 , dp[i-1][j-1]+1.
-
上面这三种操作都能让 子串A和B变得相同,那么取这三个中操作次数最小的即可。
完整代码
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
dp = [[0] * (len(word2)+1) for _ in range(len(word1)+1)]
for i in range(len(word1)+1):
dp[i][0] = i
for i in range(len(word2)+1):
dp[0][i] = i
for i in range(1,len(word1)+1):
for j in range(1,len(word2)+1):
if word1[i-1] == word2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1)
print(dp)
return dp[-1][-1]