Leetcode每日一题——72. 编辑距离。动态规划

题目链接:

力扣

题目描述:

给你两个单词 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]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翔空中,策人生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值