力扣LeetCode72.编辑距离及Python源码实现

本题为LeetCode题库中第72题编辑距离,难度困难,题目请参考这里。这道题应该是字符串处理,自然语言算法工程师必做一道题目,很久都没想到解决思路突然有了灵感。

力扣LeetCode72.编辑距离及Python源码实现_第1张图片

标准的DP题目,这道题的难点还是在于如何去定义状态转移方程,前面分享的那道地下城游戏也是难在定义状态转移方程,所以如何去定义才是最重要的!然后就是推导出如何变换。这种题很容易就想复杂了,而且情况一多根本就不知道怎么入手,这就陷入了僵局,如何跳出来?只能多刷题培养这种思维。

定义:

当字符串word1的长度为i,字符串word2的长度为j,dp[i][j]定义为将word1转为word2所使用的最少操作次数。

其实上面这个定义才是这道题最关键的部分,只要给出了定义,从定义出发来推导状态转移方程。其实做了那么多的动态规划题目,无非就是从dp[i-1][j],dp[i][j-1],dp[i-1][j-1]中找出和dp[i][j]的关系。下面我们来疏导这几个dp数组和dp[i][j]之间的关系,把关系理清楚了,状态转移方程自然也就出来了。

  • 如果字符word1[i]和字符word2[j]相同,这个时候就不需要进行任何操作,往前推即有dp[i][j] = dp[i-1][j-1],这里没有进行操作也就不用加一。
  • 如果字符word1[i]和字符word2[j]不相同,这个时候就必须要调整,即删除、替换、插入三种操作。
  1. 把字符word1[i]替换与word2[j]相同,即有dp[i][j] = dp[i-1][j-1]+1,也就是说该位置字符相等了,考虑word1前i-1个字符和word2前j-1个字符的最小编辑距离就行了;
  2. 在字符word[i]末尾插入一个与word2[j]相同的字符,即有dp[i][j] = dp[i][j-1]+1,也就是要考虑前word2前j-1个字符和word1前i个字符的最小编辑距离就行了;
  3. 把字符word[i]删除了,即有dp[i][j] = dp[i-1][j]+1,也就是考虑word1前i-1个字符和word2前j个字符的最小编辑距离就行了。

好,通过以上的分析,其实状态转移方程就出来了,再多说一句。我们选择的操作肯定是dp[i][j]的值最小,故:

dp[i][j] = min(dp[i-1][j-1], dp[i][j-1], dp[i-1][j]) + 1,这就是本题的核心:状态转移方程。

分析完状态转移方程这道题就差不多能解出来了,当然要考虑一下特殊情况:如果其中某个字符串是空串,就不能使用这个状态转移方程计算。当一个字符串的长度为0时,那么就只能让另一个字符串一直删除或者该字符串一直插入。故在设计dp二维数组的初始值的时候要进行特殊化处理,详情看代码注释。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        n1 = len(word1) #获取长度
        n2 = len(word2)

        dp = [[0 for _ in range(n2+1)] for _ in range(n1+1)] #以第一个字符串长度为行数,第二个字符串长度为列数
         
        for i in range(1, n1+1):
            dp[i][0] = dp[i-1][0] + 1 #对于某个字符串长度为0的特殊处理
        for j in range(1, n2+1):
            dp[0][j] = dp[0][j-1] + 1
        
        for i in range(1, n1+1):
            for j in range(1, n2+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], dp[i-1][j])+1 #如果不相等则选择一个最小
        return dp[-1][-1]

总结:这道题虽然也是hard题目,而且做了很久没思路,主要原因在于怎么去定义这个状态转移方程,这是dp的核心也是关键所在。拿上次的地下城游戏来说,难度不仅在于要设计状态转移方程,还要考虑从前往后还是从后往前,而且还要保证在每个位置的生命值至少为1,可以说难度还是相当大。本题核心在于考虑对某个位置的字符不同时的处理从而推导出状态转移方程。

分享知乎上一个动态规划的讲解。

摆在刷题面前的三座大山:递归、动态、回溯。

 

 

你可能感兴趣的:(LeetCode)