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