1、斐波那契数列
1.1 爬楼梯
70. 爬楼梯(Easy)
走n阶楼梯的方法有两种,1、先走 1 级台阶,再走 n-1 级台阶;2、先走 2 级台阶,再走 n-2 级台阶
f(n) = f(n-1) + f(n-2)
class Solution:
def climbStairs(self, n: int) -> int:
pre, cur= 1, 1
for i in range(1, n):
tmp = cur
cur = pre + cur
pre = tmp
return cur
1.2 强盗抢劫
198. 打家劫舍(Medium)
每个房间财产为 nums[0]……nums[n-1]。
假设 0 至 x 间房获得的最大财产为 f(x)。
f(x) = max(f(x-1),f(x-2)+nums[x])
如果不拿房间 x 的财产,可以获得的总财产为 f(x-1),如果拿房间 x 的财产,可以获得的总财产为 f(x-2)+nums[x],二者取最大值就是 0-x 房间可以获得的最大财产。
class Solution:
def rob(self, nums: List[int]) -> int:
pre, cur = 0, 0
for num in nums:
tmp = cur
cur = max(cur, pre + num)
pre = tmp
return cur
1.3 环形街道抢劫
213. 打家劫舍 II(Medium)
转换为非环形街道求解,求从第一个到倒数第二个房子的最大财宝数。再求第二个到倒数第一个房子的最大财宝数。取二者最大值。
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 0: return 0
if n== 1: return nums[0]
def rob_line(nums, left, right):
pre, cur = 0, 0
for i in range(left, right+1):
tmp = cur
cur = max(cur, pre + nums[i])
pre = tmp
return cur
return max(rob_line(nums, 1, n-1), rob_line(nums, 0, n-2))
2、矩阵路径
2.1 矩阵的最小的路径和
64. 最小路径和(Medium)
找出矩阵的左上角到右下角的最小路径和,每次只能向右和向下移动。
问题分析:
子问题:第 i 行第 j 列的元素到右下角的路径和。
递推公式:
grid[i][j] = min( grid[i][j+1], grid[i+1][j] ) + grid[i][j];
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
row = len(grid)
col = len(grid[0])
for i in range(1, col):
grid[0][i] += grid[0][i-1]
for i in range(1, row):
grid[i][0] += grid[i-1][0]
for i in range(1, row):
for j in range(1, col):
grid[i][j] += min(grid[i][j-1], grid[i-1][j])
return grid[-1][-1]
2.2 矩阵的总路径数
62. 不同路径(Medium)
方法一:动态规划
子问题:从 i 行 j 列走到矩阵左上角多少种走法;
边界条件:在第一行或者第一列,只有一种走法;
递推公式:vec[i][j] = vec[i][j-1] + vec[i-1][j]; 表示从某一点到左上角的走法等于,先向左走一步的走法和先向上走一步走法之和。
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if m == 1 or n == 1:
return 1
vec = [[1] * n for i in range(m)]
for i in range(1, m):
for j in range(1, n):
vec[i][j] = vec[i - 1][j] + vec[i][j - 1]
return vec[-1][-1]
方法二:组合数学
这道题可以用公式求解,是组合问题。机器人总共移动的次数 S=m+n-2,向下移动的次数 D=m-1,从 S 个位置中取出 D 个,有多少种方法,这个问题的解为 C(S, D)。
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
S = m+n-2
D = m-1
res = 1
for i in range(1, D+1):
res = res * (S-D+i)/i
return int(res)
或者
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
return comb(m+n-2, m-1)
3、数组区间
3.1 数组区间和
303. 区域和检索 - 数组不可变(Easy)
class NumArray:
def __init__(self, nums: List[int]):
n = len(nums)
self.sum = [0] * n
for i in range(n):
self.sum[i] = self.sum[i-1] + nums[i]
def sumRange(self, left: int, right: int) -> int:
return self.sum[right] - self.sum[left-1] if left > 0 else self.sum[right]
3.2 数组中等差递增子区间的个数
413. 等差数列划分(Medium)
方法一:动态规划
dp[i] 表示以 A[i] 结尾的等差区间的数量;
如果{ A[i-3],A[i-2],A[i-1] } 为等差区间,以 A[i-1] 结尾的等差区间数量为 dp[i-1]。并且 A[i]-A[i-1]==A[i-1]-A[i-2],得出以 A[i] 结尾的等差区间数量为 dp[i]=dp[i-1]+1。
class Solution:
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
n = len(nums)
vec = [0] * n
for i in range(2, n):
if nums[i-1] - nums[i-2] == nums[i] - nums[i-1]:
vec[i] = vec[i-1] + 1
return sum(vec)
时间复杂度:O(n)
空间复杂度:O(n)
动态规划的空间优化,计算 dp[i] 时每次只用到 dp[i-1],所有没有必要使用数组。
class Solution:
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
n = len(nums)
dp = 0
res = 0
for i in range(2, n):
if nums[i-1] - nums[i-2] == nums[i] - nums[i-1]:
dp += 1
res += dp
else:
dp = 0
return res
时间复杂度:O(n)
空间复杂度:O(1)
方法二:用公式计算
从前面的分析可以看出,当出现一个等差区间时,从数组中第三个数开始,以该数结尾的区间数分别为1,2,3,4……,所以可以直接用公式求解。
class Solution:
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
n = len(nums)
count = 0
res = 0
for i in range(2, n):
if nums[i-1] - nums[i-2] == nums[i] - nums[i-1]:
count += 1
else:
res += (count+1)*count//2
count = 0
return res + (count+1)*count//2
4、分割整数
4.1 分割整数的最大乘积
343. 整数拆分(Medium)
子问题:求和为 i 的整数分割后的最大乘积,用 dp[i] 表示
边界条件:dp[1]=1
递推公式:dp[i]=max(dp[i],max(dp[i-j]*j,(i-j)*j));
- dp[i] 表示本次不分割的最大乘积
- dp[i-j]*j 表示本次分割出一个 j,剩下的和为 i-j 再分割
- (i-j)*j 表示本次分割出一个 j,剩下的不分割的最大乘积
class Solution:
def integerBreak(self, n: int) -> int:
dp = [1] * (n+1)
for i in range(2, n+1):
for j in range(1, i):
dp[i] = max(dp[i], dp[i-j] * j, (i-j)*j)
return dp[n]
4.2 将一个数分解为整数的平方和
279. 完全平方数(Medium)
问题分析:
子问题分解:将 i 分解为整数的平方,最少可以分解为几个数的平方,用 dp[i] 表示。
初始化:dp[i] = i,表示全部分解为 1 的和。
递推条件:dp[i] = min(dp[i], dp[i-j*j]+1) { j>=1 && j*j<=i }
class Solution:
def numSquares(self, n: int) -> int:
dp = [0] * (n+1)
for i in range(n+1):
dp[i] = i
for i in range(1, n+1):
rg = int(sqrt(i))
for j in range(1, rg+1):
dp[i] = min(dp[i], dp[i-j*j] + 1)
return dp[n]
4.3 分割整数构成字母字符串
91. 解码方法(Medium)
问题分析:
子问题:到第 i 个字符可以解码的总方法,用 dp[i] 表示。
边界条件:dp[0]=1,如果第一个字符为 ‘0’,dp[1]=0,否则为 1。
递推公式:
先看挑出一个字符:
- 第 i 个字符不是 0(dp[i-1]!=0),一定可以挑出一个字符,dp[i]+=dp[i-1]
再看挑出两个字符:
- 第 i-1 个字符是 0(dp[i-2] == 0),挑不出两个字符,进行下一轮判断
- 第 i-1 个字符不是 0,看第 i-1,i 个字符组成的数字,如果小于等于26,表示可以挑出两个字符,dp[i]+=dp[i-2]
其他情况下无法挑出字符,为 0,不予改变。
class Solution:
def numDecodings(self, s: str) -> int:
n = len(s)
dp = [0] * (n+1)
dp[0] = 1
dp[1] = 0 if s[0] == '0' else 1
for i in range(2, n+1):
if s[i-1] != '0':
dp[i] += dp[i-1]
if s[i-2] == '0':
continue
tmp = s[i-2] + s[i-1]
if tmp <= '26':
dp[i] += dp[i-2]
return dp[n]
5、最长上升子序列
5.1 最长上升子序列
91. 解码方法(Medium)
方法一:动态规划
找出状态转移方程:
maxLen(k) 表示以 ak 做为“终点”的最长上升子序列的度,那么:
- 初始状态:maxLen (1) = 1
- maxLen (k) = max { maxLen (i):1<= i< k 且 ai< ak 且 k≠1 } + 1
若找不到这样的 i,则 maxLen (k) = 1
maxLen (k) 的值,就是在 ak 左边,“终点”数值小于 ak,且长度最大的那个上升子序列的长度再加 1。
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
dp = [1] * (n+1)
for i in range(1, n):
for j in range(i):
if nums[i] > nums[j]:
dp[i+1] = max(dp[i+1], dp[j+1]+1)
return max(dp)
- 时间复杂度:O(n2)
- 空间复杂度:O(n)
5.2 数对可以组成的最长链
646. 最长数对链(Medium)
方法一:贪心
对所有的数对按照第二个数的大小进行排序。第一步选结束数字最小的那个数对。 然后,每步都选和上一个选中的数对不冲突且结束数字最小的那个数。
class Solution:
def findLongestChain(self, pairs: List[List[int]]) -> int:
res = 1
pairs.sort(key=lambda x : x[1])
tail = pairs[0][1]
for item in pairs:
if item[0] > tail:
res += 1
tail = item[1]
return res
方法二:动态规划
对所有的数对按照第一个数的大小进行排序。子问题为数对 pairs[i](i=1, 2, 3…N)为终点的最长链。a[i] 表示已数对 pairs[i]为终点的链长度。
- 初始状态:a[i] = 1
- a[i] = max { a[i], a[j]+1} 0<= j< i 且 pairs[i][0]>pairs[j][1]
再寻找 a[i] 的最大值。
class Solution:
def findLongestChain(self, pairs: List[List[int]]) -> int:
pairs.sort()
n = len(pairs)
dp = [1] * (n+1)
for i in range(1, n):
for j in range(i):
if pairs[i][0] > pairs[j][1]:
dp[i+1] = max(dp[i+1], dp[j+1] + 1)
return max(dp)
5.3 最长摆动子序列
376. 摆动序列(Medium)
方法一:动态规划
子问题:求以第 i 个元素结尾的最长摆动子序列,
up[i] 指的是到目前为止所获得的最长摆动子序列的长度,其中第 i 个元素是摆动子序列的最后一个元素并且以上升的摆动结束。
类似地,down[i] 指的是到目前为止获得的最长摆动子序列的长度,其中第 i 个元素作为摆动子序列的最后一个元素并且以下降摆动结束。
边界条件:up[i] = down[i] = 1(每个元素自身都可以作为一个子序列)
递推公式:
up[i] = max(up[i], down[j]+1) (j=0, 1 ,……i-1)
down[i] = max(down[i], up[j]+1) (j=0, 1 ,……i-1)
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
n = len(nums)
up = [1] * n
down = [1] * n
for i in range(1, n):
for j in range(i):
if nums[i] > nums[j]:
up[i] = max(up[j], down[j] + 1)
elif nums[i] < nums[j]:
down[i] = max(down[j], up[j] + 1)
return max(max(up), max(down))
时间复杂度:O(n2)
空间复杂度:O(n)
方法二:线性动态规划
子问题:前 i 个元素所组成的最长摆动子序列,
- 到第 i 个元素,最后一个元素以上升的摆动结束的长度为 up[i]
- 到第 i 个元素,最后一个元素以下降的摆动结束的长度为 down[i]
边界条件:
- up[0] = down[0] = 1;
递推公式:文章来源地址https://www.toymoban.com/news/detail-438350.html
- 如果 nums[i] > nums[i-1],意味着序列将上摆,前一个序列必然下降,得出:up[i] = down[i-1] + 1, down[i] = down[i−1];
- 如果 nums[i] < nums[i-1],意味着序列将下降,前一个序列必然上摆,得出down[i] = up[i−1]+1, up[i] = up[i-1];
- 如果 nums[i] == nums[i-1],上摆下降序列都不变down[i] = down[i-1],up[i] = up[i-1]。
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
n = len(nums)
up = [1] * n
down = [1] * n
for i in range(1, n):
if nums[i] > nums[i-1]:
up[i] = max(up[i-1], down[i-1] + 1)
down[i] = down[i-1]
elif nums[i] < nums[i-1]:
down[i] = max(down[-1], up[i-1] + 1)
up[i] = up[i-1]
else:
up[i] = up[i-1]
down[i] = down[i-1]
return max(up[n-1], down[n-1])
6、最长公共子序列
1143. 最长公共子序列(Medium)
- 状态定义
定义 dp[i][j] 表示 text1[0:i-1] 和 text2[0:j-1] 的最长公共子序列。(注:text1[0:i-1] 表示的是 text1 的第 0 个元素到第 i - 1 个元素,两端都包含)
- 状态的初始化
当 i = 0 时,dp[0][j] 表示的是 text1 中取空字符串跟 text2 的最长公共子序列,结果肯定为 0。
当 j = 0 时,dp[i][0] 表示的是 text2 中取空字符串跟 text1 的最长公共子序列,结果肯定为 0。
所以,当 i = 0 或者 j = 0 时,dp[i][j] 初始化为 0.
- 状态转移方程
当 text1[i - 1] == text2[j - 1] 时,说明两个子字符串的最后一位相等,所以最长公共子序列又增加了 1,所以 dp[i][j] = dp[i - 1][j - 1] + 1。
当 text1[i - 1] != text2[j - 1] 时,说明两个子字符串的最后一位不相等,那么此时的状态 dp[i][j] 应该是 dp[i - 1][j] 和 dp[i][j - 1] 的最大值。
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
row, col = len(text1), len(text2)
dp = [[0]* (col+1) for _ in range(row+1)]
for i in range(1, row+1):
for j in range(1, col+1):
if text1[i-1] == text2[j -1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[row][col]
7、0-1背包
7.1 将数组分为和相等的两部分
416. 分割等和子集(Medium)
方法一:
问题分解:dp[i][j]表示从前i个数字中取数字,和不超过 j,最大的和是多少。
边界条件:dp[0][j]=0; dp[i][0]=0;
递推公式:dp[i][j] = max( dp[i-1][j], dp[i-1][j-nums[i]]+nums[i] );
dp[i][j]表示前i个数字中取数子,背包容量为 j,可以得到的最大容量是多少,如果 dp[n][sum/2]==sum/2,即背包容量为 sum/2,从所有的数字中选,得到的最大和为 sum/2,说明可以将数组分为和相等的两份。
class Solution:
def canPartition(self, nums: List[int]) -> bool:
n = len(nums)
total = sum(nums)
if total % 2:
return False
dp = [0] * (total//2 + 1)
for i in range(n):
for j in range(total//2, nums[i]-1, -1):
dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
return dp[total//2] == total//2
方法二:
问题分解:dp[i][j]表示从前 i 个数字中取数字,和为 j 是否可行,可行为 true,不可行为 false。
边界条件:dp[i][0]=true;
递推公式:dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
class Solution:
def canPartition(self, nums: List[int]) -> bool:
n = len(nums)
total = sum(nums)
if total % 2:
return False
dp = [False] * (total//2 + 1)
dp[0] = True
for i in range(n):
for j in range(total//2, nums[i]-1, -1):
dp[j] = dp[j] or dp[j-nums[i]]
return dp[total//2]
7.2 为一组数指定正负号使其和为目标值
494. 目标和(Medium)
将数分为正负两部分,分别为 P 和 N,
sum( P)-sum(N)=target
sum( P)+sum(N)+sum( P)-sum(N)=target+sum( P)-sum(N)
2*sum( P)=target+sum(nums)
问题变为从数组种任意选取数值,使得和为 (target+sum(nums))/2,一共有多少种选法。
问题分解:从前 i 个数字中任意选取,使得和为 j,一共有多少种选法,用 dp[i][j] 表示
边界条件:dp[i][0]=1,dp[0][j]=0 { j=1,2,3……}
递推公式:dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i]]
空间优化,二维数组变为一维数组
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
total = sum(nums)
if total < target or (total + target) % 2 == 1:
return 0
w = (total + target)//2
if w < 0:
return 0
dp = [0] * (w + 1)
dp[0] = 1
for _, num in enumerate(nums):
for j in range(w, num-1, -1):
dp[j] += dp[j-num]
return dp[w]
7.3 从字符串集合中选尽可能多的字符串并保证0和1个数不超过给定值
474. 一和零(Medium)
问题分析:
可以理解为一个多维费用的 0-1 背包问题,只不过这里有两个背包,分别表示 0 的数量和 1 的数量。从集合中选取字符串,保证所有 0 的数量不大于 m,所有 1 的数量不大于 n,最多可以选多少个字符串。
问题分解:从集合中前 k 个字符串中字符,使得 0 的数量不大于 i,1 的数量不大于 j,最多可以选多少个字符串。用 dp[k][i][j] 表示。
边界条件:dp[k][0][j]=0; dp[k][i][0]=0;
递推公式:dp[k][i][j]=max( dp[k-1][i][j], dp[k-1][i-zero][j-one] ); // zero表示字符串 k 中 0 的个数,one 表示字符串 k 中 1 的个数。
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
Len = len(strs)
dp = [[[0 for _ in range(n+1)] for _ in range(m+1)] for _ in range(Len+1)]
for k in range(1, Len+1):
cnt0 = strs[k-1].count('0')
cnt1 = strs[k-1].count('1')
for i in range(m+1):
for j in range(n+1):
dp[k][i][j] = dp[k-1][i][j]
if cnt0 <= i and cnt1 <= j:
dp[k][i][j] = max(dp[k-1][i][j], dp[k-1][i-cnt0][j-cnt1] + 1)
return dp[Len][m][n]
空间优化,三维数组变为二维数组,逆序遍历
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
dp = [[0 for _ in range(n+1)] for _ in range(m+1)]
for s in strs:
cnt0 = s.count('0')
cnt1 = s.count('1')
for i in range(m, cnt0-1, -1):
for j in range(n, cnt1-1, -1):
dp[i][j] = max(dp[i][j], dp[i-cnt0][j-cnt1] + 1)
return dp[m][n]
7.4 用最少的硬币拼出总额
322. 零钱兑换(Medium)
我们采用自下而上的方式进行思考。仍定义 dp[i] 为组成金额 i 所需最少的硬币数量。
初始值:dp[0] = 0
递推条件:dp(i) = min(dp[i], dp[i-j] + 1)
其中 j 代表的是硬币面值
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for x in range(coin, amount +1):
dp[x] = min(dp[x], dp[x-coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
7.5 找零钱的组合
8、买卖股票
8.1 需要冷却的股票交易
309. 最佳买卖股票时机含冷冻期(Medium)
解题思路:
定义三种状态
- nostock:没有股票,可以买进
- havestock:拥有股票,可以出售
- cool:正在冷却
更新三种状态
- 此时是冷却状态,可以是前面就是冷却状态,继续保持,也可以是卖了股票,变成冷却状态 cool = max(cool, havestock + prices[i]);
- 此时拥有股票,可以是前面就有股票,继续拥有,也可以是从没有股票的状态下买了股票 havestock = max(havestock, nostock - prices[i]);
- 此时没有股票,可以是前面也没有股票,继续保持,也可以是冷却状态变成没有股票的状态(冷却结束)nostock = max(nostock, cool);
class Solution:
def maxProfit(self, prices: List[int]) -> int:
nostock = 0
havestock = -prices[0]
cool = 0
for price in prices:
tmp = cool
cool = max(tmp, havestock+price)
havestock =max(havestock, nostock-price)
nostock = max(nostock, tmp)
return max(nostock, cool)
8.2 收交易费的交易
714. 买卖股票的最佳时机含手续费(Medium)
解题思路:
定义两种状态,买进(buy)和卖出(sell),buy 表示此时买进获得的利润,sell 表示此时卖出得到的利润。
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
sell = 0
buy = -prices[0]
for price in prices:
sell = max(sell, buy+price-fee)
buy = max(buy, sell-price)
return max(sell, buy)
8.3 最多进行两次交易
123. 买卖股票的最佳时机 III(hard)
解题思路:
定义四种状态:第一次买(buy1);第一次卖(sell1);第二次买(buy2);第二次卖(sell2)
class Solution:
def maxProfit(self, prices: List[int]) -> int:
buy1 = -prices[0]
sell1 = 0
buy2 = -prices[0]
sell2 = 0
for price in prices:
buy1 = max(buy1, -price)
sell1 = max(sell1, buy1 + price)
buy2 = max(buy2, sell1 - price)
sell2 = max(sell2,buy2 + price)
return max(sell1, sell2)
8.4 只能进行 k 次的股票交易
188. 买卖股票的最佳时机 IV(hard)
略
最长回文子序列
516. 最长回文子序列(Medium)
-
确定状态
dp[i][j]:以下标 i 为起始点,下标 j 为结束点的子串的最长回文子序列长度。 -
状态转移方程
当 s[i] == s[j] 时,dp[i][j] = dp[i+1][j-1] + 2
当 s[i] != s[j] 时,dp[i][j] = max(dp[i+1, j], dp[i, j-1]) -
算法流程
初始 dp[][] = 0
dp[i][i] = 1 -
返回:return dp[0][n-1]
从最后一行开始计算,每行从左向右计算
class Solution(object):
def longestPalindromeSubseq(self, s):
n = len(s)
dp = [[0]*n for _ in range(n)]
for i in range(n):
dp[i][i] = 1
for i in range(n-1, -1, -1):
for j in range(i+1, n):
if s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2
if s[i] != s[j]:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
return dp[0][n-1]
交错字符串
97. 交错字符串(Hard)
方法:动态规划
首先如果 len(s1) + len(s2) != len(s3) 返回 False
定义:dp[i][j] 表示 s1 的前 i 个元素和 s2 的前 j 个元素是否能交错组成 s3 的前 i+j 个元素。
边界条件: d[0][0] =True文章来源:https://www.toymoban.com/news/detail-438350.html
递推公式:
- 如果 s1 的第 i 个元素和 s3 的第 i+j 个元素相等,那么 s1 的前 i 个元素和 s2 的前 j 个元素是否能交错组成 s3 的前 i + j 个元素取决于 s1 的前 i-1 个元素和 s2 的前 j 个元素是否能交错组成 s3 的前 i+j-1 个元素,即此时 dp[i][j] 取决于 dp[i-1][j],在此情况下如果 dp[i-1][j] 为真,则 dp[i][j] 也为真。
- 同样的,如果 s2 的第 j 个元素和 s3 的第 i + j 个元素相等并且 dp[i][j-1] 为真,则 dp[i][j] 也为真。
- 于是我们可以推导出这样的动态规划转移方程:
dp[i][j] = (d[i-1][j] and s1[i-1] == s3[i+j-1]) or (dp[i][j-1] and s2[j-1] ==s3[i+j-1])
class Solution(object):
def isInterleave(self, s1, s2, s3):
n1, n2, n3 = len(s1), len(s2), len(s3)
if n1 + n2 != n3:
return False
dp = [[False] * (n2+1) for _ in range(n1 + 1)]
dp[0][0] = True
for i in range(n1+1):
for j in range(n2+1):
if i > 0:
dp[i][j] |= (dp[i-1][j] and s1[i-1] == s3[i+j-1])
if j > 0:
dp[i][j] |= (dp[i][j-1] and s2[j-1] == s3[i+j-1])
return dp[n1][n2]
到了这里,关于Leetcode题解-算法-动态规划(python版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!