cxyxz

问题描述

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

示例:

输入:word1 = "horse", word2 = "ros"

输出:3

解释:

horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

分析问题

根据题目要求,你可以对任意一个单词进行增、删、改三种操作,题目给定两个单词,所以一共有2*3=6中操作。但是这里包含了一些重复的情况,假设给定的单词是A和B。

  • 对单词A删除一个字符和对单词B增加一个字符是等价的。比如单词A是“abc”,单词B为“bc”时,我们既可以删除单词A的第一个字符a,得到相同的“bc”,也可以在单词B的开头添加一个字符a,得到相同的“abc”。
  • 同理,对单词B删除一个字符和对单词A插入一个字符也是等价的。
  • 对单词A替换一个字符和对单词B替换一个字符也是等价的。

所以本质上不同的操作只有三种,即

  • 在单词A中插入一个字符
  • 在单词A中删除一个字符
  • 修改单词A中的一个字符

这样我们可以把原问题拆分成若干个子问题,我们假设A="horse",B="ros"。下面来看一下如何具体操作。

  • 在单词A中插入一个字符:如果我们知道“horse”到“ro”的编辑距离为a,那么“horse”到“ros”的编辑距离不会超过a+1。因为我们只需要在“horse”的末尾添加一个字符s即可。
  • 在单词A中删除一个字符:如果我们知道“hors” 到 “ros”的编辑距离为b,那么“horse”到 “ros”的编辑距离不会超过 b + 1。因为我们只需要删除“horse”的最后一个字符e即可。
  • 修改单词A中的一个字符:如果我们知道“hors”到“ro”的编辑距离为c,那么“horse”到 “ros”的编辑距离不会超过 c + 1。因为我们只需要把“horse”的最后一个字符e修改为s即可。

那么从“horse”变成“ros”的最小编辑距离为min(a+1,b+1,c+1)

所以这道题就可以使用动态规划的方式来求解。我们假设dp[i] [j] 表示word1的前i个字符,变换到word2的前j个字符的最小编辑距离。根据上面的分析,我们可以知道它是由以下三种状态转移的最小值过来的。即

  • dp[i] [j] = dp[i] [j-1] +1,即在单词A中增加一个字符
  • dp[i] [j] = dp [i-1] [j] + 1,即在单词A中删除一个字符
  • dp[i] [j] = dp[i-1] [j-1] +1,即在单词A中替换一个字符

所以动态转移方程为:

若A、B的最后一个字符相同 **dp[i] [j] = min(dp[i] [j-1]+1,dp [i-1] [j]+1,dp[i-1] [j-1]) **。

若A、B的最后一个字符不相同 dp[i] [j] = min(dp[i] [j-1]+1,dp [i-1] [j]+1,dp[i-1] [j-1]+1)

下面我们来看一下边界情况,一个空串和一个非空串的编辑距离dp[i] [0] = i, dp[0] [j] =j。dp[i] [0] 相当于对word1执行了i次删除操作,dp[0] [j]相当于对word1进行了j次插入操作。

下面我们来看一下代码实现。

class Solution:
    def minDistance(self, word1, word2):
        #求出单词的长度
        n = len(word1)
        m = len(word2)

        #判断是否是空串
        if n==0:
            return m
        if m==0:
            return n

        #定义状态转移矩阵
        dp = [[0] * (m + 1) for _ in range(n + 1)]

        #处理边界条件
        for i in range(n + 1):
            dp[i][0] = i
        for j in range(m + 1):
            dp[0][j] = j

        #填充状态转移矩阵
        for i in range(1, n + 1):
            for j in range(1, m + 1):
                #上边状态转移过来
                up = dp[i - 1][j] + 1
                #左边状态转移过来
                left = dp[i][j - 1] + 1
                #左上的状态
                left_up = dp[i - 1][j - 1]
                #如果最后一个字符不相同,left_up需要加1
                if word1[i - 1] != word2[j - 1]:
                    left_up += 1
                dp[i][j] = min(left, up, left_up)

        return dp[n][m]

该算法的时间复杂度和空间复杂度都是O(n*m)。

最后

送大家几本比较不错的算法书籍~

小争哥数据结构与算法
链接:https://pan.baidu.com/s/19Jk_G_-QTnGb3GRyzbENgA

密码:keis

谷歌大佬LeetCode刷题指南
链接:https://pan.baidu.com/s/1vtRIsVltTxmIioqqkeSS5g

密码:r3xg

算法小抄
链接:https://pan.baidu.com/s/1rU_T6GRZ-WmV9QFmnJfCBg

密码:unh5

相关文章: