题目链接:
题目描述:
给你两个单词 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 由小写英文字母组成
解题思路:
编辑距离可以理解为为了使2个序列一模一样,对2个序列的修改(包括增删改)次数之和
那么题意就是要我们求最小编辑距离了
遇到2个字符串的比较问题,用动态规划的话一般都是设置2个维度的dp数组分别表示2个字符串的状态。所以这里可以设置dp[i][j]表示分别以word1[i-1]、word2[j-1]结尾的字符串的最小编辑距离
dp四部曲:
确定dp数组:dp[i][j]表示分别以word1[i-1]、word[j-1]结尾的字符串的最小编辑距离
确定递推关系:对于word1[i-1]、word[j-1]有2种状态:
如果相等,那么dp[i][j]=dp[i-1][j-1],即编辑距离与上一对字符的相同。
如果不等,又分为3种情况,增删改。但是增和删是等效的,可归为一种情况,例如对于'ab','a',增加b和删除b都可以使2者相同,且2种情况的编辑距离都为1,所以从结果来说这2种操作是等效的;所以实际上不等就2种情况:
删的话可以选择删掉word1[i-1],那么此时dp[i][j]=dp[i-1][j]+1,即在使分别以word[i-2],word[j-1]结尾的字符串相等的情况下删掉word[i-1],使分别以word[i-1],word[j-1]结尾的字符串相等(此时word[i-1]相当于空字符串);如果选择删掉word2[j-1]的话dp[i][j]=dp[i][j-1]+1。
改的话就是在在使分别以word[i-2],word[j-2]结尾的字符串相等的情况下改word1[i-1]或者word2[j-1]使分别以word[i-1],word[j-1]结尾的字符串相等,那么dp[i][j]=dp[i-1][j-1]+1了
由于取最小编辑距离,所以等的情况dp[i][j]=dp[i-1][j-1],不等的时候dp[i][j]=min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1])+1
初始化dp数组:由递推公式可知,dp[i][j]有3个来源:左边、上方、左上方,所以初始化要初始化dp[0][x]和dp[x][0],根据定义,dp[0]表示跟下标为-1的字符串比较,这时就可认为是在跟空字串比较了,那么使别的字符串跟空字符串相同,最小编辑距离肯定就是把字符串内容全删了,也就是len(string)了。除了dp[0][x]和dp[x][0],其他都初始化为0
确定遍历顺序:dp[i][j]有3个来源:左边、上方、左上方。所以遍历顺序应该是从上到下从左到右
代码实现如下:
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
if len(word1) == 0: return len(word2)
if len(word2) == 0: return len(word1)
dp = [[0] * (len(word2) + 1) for i in range(len(word1) + 1)]#行是1,列是2
for i in range(len(dp)): dp[i][0] = i
for i in range(len(dp[0])): 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], dp[i][j - 1], dp[i - 1][j - 1]) + 1
return dp[-1][-1]
考虑到dp[i][j]有3个来源:左边、上方、左上方。所以可以用二维滚动数组降低空间复杂度,代码如下:
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
if len(word1) == 0: return len(word2)
if len(word2) == 0: return len(word1)
dp = [[0] * (len(word2) + 1) for i in range(2)]#行是1,列是2
for i in range(len(dp)): dp[i][0] = i
for i in range(len(dp[0])): 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[1][j] = dp[0][j - 1]
else:
dp[1][j] = min(dp[0][j], dp[1][j - 1], dp[0][j - 1]) + 1
dp[0] = [k for k in dp[1]]
dp[1][0] += 1#更新word1[i]与word2[-1](空字符串)的比较结果
return dp[-1][-1]