适用情况
求最值,有子序列相关
模式
- dp表法
思路
- 根据题目建立dp表,明确dp[i]的含义。
- 设置 base case ,初始化 dp 数组。
- 运用数学归纳法的思想,假设 dp[0…i-1] 都已知,想办法求出 dp[i]。动态找dp[i], dp[j]的变化关系,在什么条件下满足题目要求的变化。可以把初始化的值也考虑进来
(但如果无法完成这一步,很可能就是 dp 数组的定义不够恰当,需要重新定义 dp 数组的含义;或者可能是 dp 数组存储的信息还不够,不足以推出下一步的答案,需要把 dp 数组扩大成二维数组甚至三维数组。)
def f(s1, s2):
<1.创建dp表(初始化)>
dp = [[0 for i in range(len(s2)+1)] for j in range(len(s1)+1)]
<2.循环修改dp表>
for i in range(1,len(s1)+1): # 大循环表示列
for j in range(1,len(s2)+1): # 小循环表示行
<3.如果相等>
if s1[i-1] == s2[j-1]:
dp[i][j] =
<4.如果不等>
else:
dp[i][j] = 最值()
return dp[-1][-1]
- 备忘录法 递归
思路
- 找原问题和子问题的变化量
- 根据题目确定函数的因变量
- 确定状态转移方程,子问题可以做出什么选择得到原问题
<1.定义备忘录>
memo = dict()
def f(n, arr):
<2.查备忘录:避免重复>
if n in memo: return memo[n]
<3.递归中止条件>
if n == 0: return 0
if n < 0: return -1
res = float('INF')
for i in arr:
<4.子问题无解>
if f(n - i) == -1: continue
<5.动态转移方程递归>
res = min(res, 1 + f(n - i))
<6.记入备忘录>
memo[n] = res if res != float('INF') else -1
return memo[n]
例题1 双字符二维dp表
leetcode 1143. (medium) 最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
输入:text1 = “abcde”, text2 = “ace”
输出:3
解释:最长公共子序列是 “ace”,它的长度为 3。
解题思路:
创建dp表,注意dp表跟循环内ij的对应,大循环的索引i代表行,小循环的索引j代表列。
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
# <1.创建dp表(初始化)> 注意dp表的对应关系
dp = [[0 for i in range(len(text2