题目1:剪绳子的乘积最大值
给定一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]*k[1] * … *k[m]可能的最大乘积是多少?
例子:
例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
1.定义状态方程:
f(n)=max(f(i)×f(n−i)), i<=n//2f(n) =\max (f(i)\times f(n-i)),\ i<=n//2f(n)=max(f(i)×f(n−i)), i<=n//2f(n)代表绳子长度为n时,所获得最大乘积. f(n)代表绳子长度为n时, 所获得最大乘积.f(n)代表绳子长度为n时,所获得最大乘积.
从下而上计算: 先计算f(2)f(3)f(2) f(3)f(2)f(3) 再计算f(4)...f(n)f(4)...f(n)f(4)...f(n).
def maxproduct(n):
if n < 2:
return 0
if n == 2:
return 1
if n == 3:
return 2
dp = [ 0 for i in range(n+1)]
dp[1] = 1
dp[2] = 2
dp[3] = 3
❗️注意 这里dp数组中保存的与上面实际的输出不符合,
因为当长度为1,2,3时,实际上不切的时候最大,但是不符合题目要求.
但是当做递归的时候, 至少会切一刀.所以dp数组中保存他们原本的长度即可
for i in range(4, n):
maxproduct = 0
for j in range(1,i//2 + 1):⭐️# i//2+1避免重复计算
maxproduct = max(maxproduct, dp[j] * dp[i-j])
dp[i] = maxproduct
return dp[-1]
题目2:最长公共子串/子序列
最长公共子序列:
dp[i][j]dp[i][j]dp[i][j]
表示 aaa字符串的0 ~ iii的字符串与bbb字符串的0 ~ jjj的字符串的最长公共子序列
状态方程:
dp[i][j]={max{dp[i−1][j],dp[i][j−1]},A[i−1]!=B[j−1]dp[i−1][j−1]+1,A[i−1]==B[j−1]dp[i][j] = \begin{cases} \max\{dp[i - 1][j],dp[i][j - 1]\} ,& {A[i - 1] != B[j - 1]} \\ dp[i - 1][j - 1] + 1 , & {A[i - 1] == B[j - 1]} \end{cases}dp[i][j]={max{dp[i−1][j],dp[i][j−1]},dp[i−1][j−1]+1,A[i−1]!=B[j−1]A[i−1]==B[j−1]
代码:
def LCS(a,b):
dp = [[0 for _ in range(len(a)+1) ]for _ in range(2)]
flag = 0
for i in range(1,len(b)+1):
for j in range(1,len(a)+1):
if a[j-1] == b[i-1]:
dp[1-flag][j] = dp[flag][j-1] + 1
else:
dp[1-flag][j] = max(dp[flag][j],dp[1-flag][j-1])
return dp[-1][-1]
最长公共子串:
dp[i][j]dp[i][j]dp[i][j]
表示 以在aaa中以a[i]a[i]a[i]结尾的字符串与在bbb中以b[j]b[j]b[j]结尾的字符串的最长公共子串 .
状态方程:
dp[i][j]={0,A[i−1]!=B[j−1]dp[i−1][j−1]+1,A[i−1]==B[j−1]dp[i][j] = \begin{cases} 0 ,& {A[i - 1] != B[j - 1]} \\ dp[i - 1][j - 1] + 1 , & {A[i - 1] == B[j - 1]} \end{cases}dp[i][j]={0,dp[i−1][j−1]+1,A[i−1]!=B[j−1]A[i−1]==B[j−1]
代码:
def longestsustring(a,b):
max_ = 0
dp = [[0 for _ in range(len(b)+1)] for _ in range(2)]
flag = 0
for i in range(1,len(a)+1):
for j in range(1,len(b)+1):
if a[i-1] == b[j-1]:
dp[1-flag][j] = dp[flag][j-1] + 1
max_ = max(max_,dp[1-flag][j])
flag = 1 - flag
return max_
题目3: 正则匹配
Input:
s = “aab”
p = “cab”
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore it matches “aab”
Input:
s = “mississippi”
p = “misisp*.”
Output: false
dp[i][j]dp[i][j]dp[i][j] 表示p i位之前的字符串和s j位之前的字符串是否匹配.
状态方程:
dp[i][j]={s[j−1]==p[i−1] or p[i−1]==′.′,p[j−1]!=′∗′dp[i−1][j] or dp[i−2][j],p[j−1]==′∗′∣dp[i][j−1],p[j−1]==′∗′,p[i−2]==s[j−1]orp[i−2]==′.′dp[i][j] = \begin{cases} s[j-1]==p[i-1] \ or \ p[i-1]=='.' ,& {p[j-1] != ' * '} \\ dp[i-1][j] \ or \ dp[i-2][j] ,&{p[j-1]=='*'}\\ | dp[i ][j-1] , &{p[j-1]=='*'} ,& p[i-2]==s[j-1] or p[i-2]=='.'\end{cases}dp[i][j]=⎩⎪⎨⎪⎧s[j−1]==p[i−1] or p[i−1]==′.′,dp[i−1][j] or dp[i−2][j],∣dp[i][j−1],p[j−1]!=′∗′p[j−1]==′∗′p[j−1]==′∗′,p[i−2]==s[j−1]orp[i−2]==′.′
第一行: 当p[j−1]!=′∗′p[j-1] != ' * 'p[j−1]!=′∗′ 很简单, 只需要判断当前位的两个字符相不相同, 然后与之前的状态做与运算.
如图:

第二行:
p[j−1]==′∗′p[j-1] == ' * 'p[j−1]==′∗′时, 当前元素是∗*∗, 考虑∗*∗的功效. 对∗*∗之前的元素, 可以取一次,也可以不取,可以取多次.
如果取一次的话, 那么就直接将dp[i−1][j]dp[i-1][j]dp[i−1][j]的结果作为当前的结果.
如果一次也不取得话,再往上,忽略掉p中*之前的元素, 那么就是dp[i−2][j]dp[i-2][j]dp[i−2][j]作为结果.
如下图纵向的传导:

还有一种功效, 就是*使得前面的元素取多次使得匹配:
有个前提, 就是p中∗*∗之前的元素p[i−2]p[i-2]p[i−2]与当前s中的元素s[j−1]s[j-1]s[j−1]相同了或者p[i−2]p[i-2]p[i−2]干脆为. 才能考虑这种情况.
那么具体配不匹配是取决于dp[i][j−1]dp[i][j-1]dp[i][j−1] 如上图, 横向的传导.
def isMatch(s,p):
dp = [[False for _ in range(len(s)+1)] for _ in range(len(p)+1)]
dp[0][0] = True
for j in range(2,len(p)+1):
dp[j][0] = dp[j-2][0] and p[j-1] == '*'
for i in range(1,len(p)+1):
for j in range(1,len(s)+1):
if p[i-1] != '*':
dp[i][j] = dp[i-1][j-1] and (p[i-1]==s[j-1] or p[i-1]== '.')
else:
dp[i][j] = dp[i-1][j] or dp[i-2][j]
if p[i-2] == s[j-1] or p[i-2] == '.':
dp[i][j] |= dp[i][j - 1]
return dp[-1][-1]

881

被折叠的 条评论
为什么被折叠?



