动态规划解决问题:编辑距离与解码方法

动态规划是解决很多算法的不二法宝。
它的思想可以概括为:根据上一个状态的值,来判断现在状态的值。
因此,在这类题目中,重点是在找出上一个状态和现在状态的联系
具体的理论就不扯了,还是直接上题目叭

编辑距离

这道题目是leetcode上的一道困难题。
在这里插入图片描述
乍一看题目可能会感觉无从下手,因为word1和word2从直观上来看很难看出联系。
那么,我们尝试利用动态规划,找出状态之间的联系
我们令D[i][j]表示第一个字符串的前i位第二个字符串的前j位之间的编辑距离。
举个栗子,对于示例2,D[9][9]表示的就是字符串word1和word2之间的编辑距离,我们要求的就是这个。
之后,重点来了,我们该如何求D[i][j]呢?经过分析,有三种更新方式。

  • 第一个字符串的前i位变成第二个字符串的前j-1位,再将第二个字符串的第j位插进去(这时编辑距离为D[i][j-1]+1)
  • 第一个字符串的前i-1位变成第二个字符串的前j位,再将第一个字符串的第i位删掉(这时编辑距离位D[i-1][j]+1)
  • 第一个字符串的前i-1位变成第二个字符串的前j-1位,然后替换第i位字符。如果第一个字符串的第i位字符和第二位字符串的第j位相同,则不用替换(编辑距离为D[i-1][j-1]);反之,如果不相同,则需要替换(编辑距离为D[i-1][j-1]+1)

经过分析,是不是清晰多了?
也就是说,我们要求D[i][j],只用在D[i][j-1]+1、D[i-1][j]+1、D[i-1][j-1](或D[i-1][j-1])中选一个最小的就行了。
显然,D[0][j]=j, D[i][0]=i(从一个空串变为一个i位字符串需要插入i个字符)
因此,要得到结果,通过一步步迭代即可。代码如下:

class Solution:
    def minDistance(self, word1, word2):
        len1 = len(word1)
        len2 = len(word2)
        # 若两个串中有一个为0串,则返回另一个串的长度
        if len1*len2 == 0:       
            return len1 + len2
        dp = [[0]*(len2+1) for _ in range(len1+1)]
        for i in range(len1+1):
            dp[i][0] = i
        for j in range(len2+1):
            dp[0][j] = j
        for i in range(1, len1+1):
            for j in range(1, len2+1):
                # 分别对应三种情况
                tog = dp[i-1][j-1]
                rep_1 = dp[i-1][j] + 1
                rep_2 = dp[i][j-1] + 1
                if word1[i-1] != word2[j-1]:
                    tog += 1
                dp[i][j] = min(tog, rep_1, rep_2)
        return dp[len1][len2]

.

解码方法

这道题是leetcode上的一道中等题。
在这里插入图片描述
这道题倒是稍微有点眉目——用暴力方法遍历每个元素,然后挨个判断。但是稍微想想就很复杂(那些个if else哟…)
但是,我们现在有一个秘密武器,动态规划!
老规矩,先探索一下两个状态之间的关系。

我们令D[i]表示字符串中前i个字符解码方法的总数。那么,更新D[i]一共有下面种情况:

  • 当第i位为‘0’时:1、如果第i-1位为’1’或者’2‘,则此时对于第i位唯一的解码方式为第i-1位和第i位的组合(单独的’0’不能解码)。此时D[i]=D[i-2];2、如果第i-1位为其他元素,则该字符串不能进行解码(此时第i位元素既不能单独解码,也不能和第i-1位元素联合解码。此时D[i] = 0
  • 当第i-1位为’1’时:此时第i位元素既可以单独解码(D[i-1]),也可以和第i-1位元素联合解码(D[i-2]。因此D[i] = D[i-1]+D[i-2]
  • 当第i-1位为’2’时:1、若第i位元素在’1’和’6’之间,则和第二种情况一样,既可以单独解码,也可以联合解码。D[i] = D[i-1]+D[i-2];2、若第i位元素为其他元素,则不能和第i-1位元素联合解码啦,只能单独解码。此时D[i]=D[i-1]

你可能会疑惑,为什么1.1会有D[i] = D[i-2],这是因为此时第i-1个元素只能和第i个元素绑定解码,而如果这样的话原本第i-2个元素与第i-1个元素的绑定(如有)就不存在了。

哈哈,虽然繁琐了一点,不过我们总算是把所有状态都分析出来了。
另外,由于整个过程只涉及到了D[i]、D[i-1]、D[i-2],因此我们可以用三个常量来动态地表示这些值(而不用一直维持着数组D),从而节省空间。

class Solution:
    def numDecodings(self, s: str) -> int:
        if s[0] == "0":
            return 0
        curr, pre, tmp = 1, 1, 1
        len_s = len(s)
        for i in range(1, len_s):
        # temp表示D[i-1]
        # curr表示D[i]
        # pre 表示D[i-2]
            temp = curr   
            if s[i] == '0':
                if s[i-1] == '1' or s[i-1] == '2':
                    curr = pre
                else:
                    return 0
            elif s[i-1] == '1' or (s[i-1]=='2' and s[i] >= '1' and s[i] <='6'):
                curr += pre
            pre = temp
        return curr
发布了19 篇原创文章 · 获赞 1 · 访问量 710

猜你喜欢

转载自blog.csdn.net/weixin_43901558/article/details/104827120