day52【代码随想录】动态规划之总结篇

本文详细介绍了动态规划的概念,并通过一系列经典问题如斐波那契数、爬楼梯、背包问题、打家劫舍系列、股票买卖和子序列问题,阐述了动态规划的解题思路、递推公式和初始化方法,涵盖了基础到进阶的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章目录


前言

动态规划五部曲:
1、确定dp数组(dp table)以及下标的含义
2、确定递推公式
3、dp数组如何初始化
4、确定遍历顺序
5、举例推导dp数组


一、基础篇动态规划

1.1斐波那契数

1、确定dp数组以及下标的含义
dp[i]:表示第i个斐波那契数值为dp[i]
2、确定递推公式
dp[i] = dp[i-1] + dp[i-2];
3、dp数组如何初始化
dp[0] = 1;
dp[1] = 1;

1.2爬楼梯

每次可以爬 1 或 2 个台阶,有多少种不同的方法可以爬到楼顶n呢?

1、确定dp数组以及下标的含义
dp[i]:表示爬到第i个楼梯有dp[i]种方法
2、确定递推公式
dp[i] = dp[i-1] + dp[i-2];
3、dp数组如何初始化
dp[0] = 1;
dp[1] = 1;

1.3使用最小花费爬楼梯

每当爬上一个阶梯都要花费对应的体力值,一旦支付了相应的体力值,就可以选择向上爬一个阶梯或者爬两个阶梯。找出达到楼层顶部的最低花费。

1、确定dp数组以及下标的含义
dp[i]:表示爬到第i个楼梯需要的最小花费为dp[i]
2、确定递推公式
dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
3、dp数组如何初始化
dp[0] = 0,
dp[1] = 0;

1.4不同路径

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角,问总共有多少条不同的路径?

1、确定dp数组以及下标的含义
dp[i][j]:表示走到i行j列时,有dp[i][j]中方法
2、确定递推公式
dp[i][j] = dp[i - 1][j] + dp[i][j-1] 【只能从两个方向过来】
3、dp数组如何初始化
dp[0][j] = 1
dp[i][0] = 1;

1.5不同路径 II

不同路径还不够,要有障碍。

1、确定dp数组以及下标的含义
dp[i][j]:表示走到i行j列时,有dp[i][j]中方法
2、确定递推公式
多了一个前提条件就是:
if( obstacleGrid[i][j] != 1 )
dp[i][j] = dp[i - 1][j] + dp[i][j-1]
else dp[i][j] = 0
3、dp数组如何初始化 【需要考虑障碍物】
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;

1.6整数拆分 【基础题中算比较有难度的】

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回可以获得的最大乘积。

1、确定dp数组以及下标的含义
dp[i]:表示整数i可以获得的最大乘积为dp[i]
2、确定递推公式
dp[i] = max(dp[i], dp[i-j]*j , (i-j)*j);

j * (i - j) 是单纯的把整数拆分为两个数相乘
j * dp[i - j]是拆分成两个以及两个以上的个数相乘

for(int i=3;i<=n;i++){
	for(int j = 1;j<i; j++){
		dp[i] = Math.max(dp[i], dp[i-j]*j, (i-j)*j);
	}
}

3、dp数组如何初始化
dp[0] = 0;
dp[1] = 0;
dp[2] = 1;

1.7不同的二叉搜索树 【基础题中算比较有难度的】 ※

给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种

dp[3],就是 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量
元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量
元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量
元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量
有2个元素的搜索树数量就是dp[2]。
有1个元素的搜索树数量就是dp[1]。
有0个元素的搜索树数量就是dp[0]。

1、确定dp数组以及下标的含义
dp[i]:表示i个节点可以组成dp[i]棵二叉搜索树
2、确定递推公式
dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
dp[i] += dp[j - 1] * dp[i - j];
3、dp数组如何初始化
dp[0] = 1,
dp[1] = 1;

二、背包问题系列

2.1 01背包系列

二维dp遍历的时候,背包容量是从小到大,
而一维dp遍历的时候,背包是从大到小。
倒序遍历的原因:是为了保证物品i只被放入一次。

2.1.1分割等和子集 【背包能不能装满】

给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
解题思路是:target = nums数组和 如果是奇数直接return false;
如果是偶数,那么我们的背包容量就确定为target/2
如果最后dp[target/2] == target/2 return true;否则false

1、确定dp数组以及下标的含义
dp[i]:背包容量为i,所装物品的价值最大为dp[i]
2、确定递推公式
对于每一个物品来说无非就是装不装两种选择【滚动数组】
前提:if(i-nums[i]>0)
装:dp[i-nums[i]]+nums[i]
不装:dp[i]
dp[i] = Math.max(dp[i], dp[i-nums[i]]+nums[i])
3、dp数组如何初始化
dp[0] = 0;
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历

2.1.2最后一块石头的重量||【背包最多能装多少】

有一堆石头,每一回合,从中选出任意两块石头,然后将它们一起粉碎。
假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。

解题思路是:target = nums数组和
我们的背包容量就确定为target/2
最后查看背包装了多少dp[target/2]
剩余的另一堆石头:target-dp[target/2]
相减: target-dp[target/2] - dp[target/2] 即为最后结果

1、确定dp数组以及下标的含义
dp[i]:背包容量为i,所装物品的价值最大为dp[i]
2、确定递推公式
对于每一个物品来说无非就是装不装两种选择【滚动数组】
装:dp[i-stones[i]]+stones[i]
不装:dp[i]
dp[i] = Math.max(dp[i], dp[i-stones[i]]+stones[i])
3、dp数组如何初始化
dp[0] = 0;
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历

2.1.3 目标和【装满背包有多少种方法】

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。返回可以使最终数组和为目标数 S 的所有添加符号的方法数

一波数学推导:
add 【+符号的】 sub【-符号的】
sum= add+sub
target = add-sub
因此:
add = (sum+target)/2 也就是我们的背包容量 如果是奇数,说明无解,直接返回0即可。同时如果 S的绝对值已经大于sum,那么也是无解的。

1、确定dp数组以及下标的含义
dp[i]:背包容量为i,装满背包i 有dp[i]种方法
2、确定递推公式
只要搞到nums[j],凑成dp[i]就有dp[i- nums[j]] 种方法。
dp[i] += dp[i-nums[j]]
例如:dp[j],j 为5,
已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。
3、dp数组如何初始化
dp[0] = 1;
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历

求组合类问题的公式,都是类似这种:

dp[j] += dp[j - nums[i]]

2.1.4 一和零【装满这个背包最多有多少个物品】

一个二进制字符串数组 strs 和两个整数 m 和 n 。找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。

1、确定dp数组以及下标的含义
dp[i][j]:i个0 j个1时,背包中最多有dp[i][j]个子集
2、确定递推公式
这个子集放还是不放
放进去:dp[i][j] = dp[i -numZero][j-numOne]+1
不放进去:dp[i][j] = dp[i][j]
dp[i][j] = Math.max(dp[i][j], dp[i -numZero][j-numOne]+1 )
3、dp数组如何初始化
dp[0] = 0;
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历

2.2 完全背包系列

每个物品都可以重复使用,因此对于背包容量的遍历由从后向前变为从前向后.
完全背包的两个for循环的先后顺序都是可以的。
== 外层for循环遍历物品,内层for遍历背包的情况,这种遍历顺序中dp[j]里计算的是组合数==

外层for遍历背包的情况,内层for循环遍历物品,这种遍历顺序中dp[j]里计算的是排列数

2.2.1零钱兑换|| 【组合数】

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

5 = 2 + 2 + 1
5 = 2 + 1 + 2
这是一种组合,都是 2 2 1。
如果问的是排列数,那么上面就是两种排列了。
组合不强调元素之间的顺序,排列强调元素之间的顺序。

1、确定dp数组以及下标的含义
dp[j]:凑成金额 j 有dp[j]种方法
2、确定递推公式
dp[j] += dp[j-coins[i]]
3、dp数组如何初始化
dp[0] = 1;
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从前向后遍历

2.2.2组合总和Ⅳ【组合数】

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

1、确定dp数组以及下标的含义
dp[j]:凑成目标 j 有dp[j]种方法
2、确定递推公式
dp[j] += dp[j-nums[i]]
3、dp数组如何初始化
dp[0] = 1;
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从前向后遍历

2.2.3爬楼梯(完全背包解法)【排列数】

正在爬楼梯。需要 n 阶你才能到达楼顶,每次可以爬 1 或 2 个台阶。有多少种不同的方法可以爬到楼顶呢?
把一次可以上的阶梯数 看作物品 weight {1,2}; 如果一个可以爬m阶都可以放入物品数组当中

1、确定dp数组以及下标的含义
dp[j]:爬到 j层 有dp[j]种方法
2、确定递推公式
dp[j] += dp[j-weight[i]]
3、dp数组如何初始化
dp[0] = 1;
4、遍历顺序
外层for循环遍历背包容量,内层for循环遍历物品

2.2.4零钱兑换【装满背包的最少物品】

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。每种硬币的数量是无限的。

1、确定dp数组以及下标的含义
dp[j]:凑成目标 j 最少的硬币个数为dp[j]
2、确定递推公式
dp[j] = Math.min(dp[j], dp[j-coins[i]]+1)
并且:只有dp[j-coins[i]]不是初始最大值时,该位才有选择的必要
3、dp数组如何初始化
dp[0] = 0;
其他值应该是最大值。
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从前向后遍历

2.2.5完全平方数【装满背包的最少物品】

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

1、确定dp数组以及下标的含义
dp[j]:凑成目标 j 最少的完全平方数为dp[j]
2、确定递推公式
dp[j] = Math.min(dp[j], dp[j-ii]+1)
并且:只有dp[j-i
i]不是初始最大值时,该位才有选择的必要
3、dp数组如何初始化
dp[0] = 0;
其他值应该是最大值。
4、遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从前向后遍历

2.2.6单词拆分【物品能不能把背包装满】※

给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

1、确定dp数组以及下标的含义
dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词。
2、确定递推公式
如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。
所以递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true。

for (int i = 1; i <= s.length(); i++) {
     for (int j = 0; j < i && !valid[i]; j++) {
         if (set.contains(s.substring(j, i)) && valid[j]) {
             valid[i] = true;
         }
     }
 }

3、dp数组如何初始化
dp[0] = true;
4、遍历顺序
外层for循环遍历背包容量,内层for循环遍历物品

三、打家劫舍系列

3.1打家劫舍

打家劫舍(力扣198)相邻的两间房子会触发警报
对于每一间房子都有偷和不偷两种状态
dp[j]:考虑下标j(包括j)以内的房屋,最多可以偷窃的金额为dp[j]。
初始化
dp[0] = nums[0];
dp[1] = max(nums[0],nums[1]);
dp[j] = max(dp[j-1],dp[j-2]+nums[j])

3.2打家劫舍||【成环】

打家劫舍||(力扣213)相邻的两间房子会触发警报并且首尾成环
分为三种情况:
情况一:不考虑首位
情况二:不考虑首
情况三:不考虑尾
情况二三都包含了情况一,因此只考虑情况二三即可
最后返回max(情况二,情况三)

3.3打家劫舍|||【树状】

打家劫舍 III(力扣337)树状
确定遍历顺序–>后序遍历
每个结点分为偷和不偷两种情况
根节点不偷
dp[0] = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
根节点偷
dp[1] = root.val + left[0] + right[0];

四、股票系列

4.1买卖股票的最佳时机【只能买卖一次】

dp[i][0]:持有股票的状态
dp[i][1]:不持有股票的状态
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[i][0] = max(dp[i-1][0], -prices[i])
dp[i][1] = max(dp[i-1][1] , dp[i-1][0]+prices[i])

4.2买卖股票的最佳时机||【可以买卖多次】

dp[i][0]:持有股票的状态
dp[i][1]:不持有股票的状态
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i])
dp[i][1] = max(dp[i-1][1] , dp[i-1][0]+prices[i])
细微变化在于持有股票状态的递推过程

4.3买卖股票的最佳时机||| 【最多买卖2次】

dp[i][0] 不操作
dp[i][1]:第一次持有股票状态
dp[i][2]:第一次不持有股票状态
dp[i][3]:第二次持有股票状态
dp[i][4]:第二次不持有股票状态
初始化
dp[0][0]= 0
dp[0][1] = -prices[0];
dp[0][2] = 0;
dp[0][3] = -prices[0];
dp[i][0] = dp[i-1][0]
dp[i][1] = max(dp[i-1][1],-prices[i])
dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i])
dp[i][3] = max(dp[i-1][3], dp[i-1][2]-prices[i])
dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i])

4.4买卖股票的最佳时机IV 【最多买卖k次】

dp[i][0] 不操作
dp[i][1]:第一次持有股票状态
dp[i][2]:第一次不持有股票状态
dp[i][3]:第二次持有股票状态
dp[i][4]:第二次不持有股票状态
……
0 ----- 2*k
奇数是持有股票状态
偶数是卖出股票状态
初始化时:
if(j%2==1) dp[0][j] = -prices[0];
奇数时:dp[i][j] = max(dp[i-1][j],dp[i-1][j-1] - prices[i])
偶数时:dp[i][j+1] = max(dp[i-1][j+1], dp[i-1][j] +prices[i])

4.5最佳买卖股票时机含冷冻期 【买卖多次,卖出后有一天是冷冻期】

dp[i][0]:持有股票的状态
dp[i][1]:保持卖出股票的状态
dp[i][2]:卖出股票的状态
dp[i][3]:冷冻期
初始化
dp[0][0] = -prices[0];
dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i], dp[i-1][3]- prices[i])
dp[i][1] = max(dp[i-1][1], dp[]i-1[3])
dp[i][2] = dp[i-1][0]+prices[i]
dp[i][3] = dp[i-1][2]

4.6买卖股票的最佳时机含手续费【买卖多次,每次都有手续费】

dp[i][0]:持有股票的状态
dp[i][1]:不持有股票的状态
dp[0][0] = -prices[0]-fee;
dp[0][1] = 0;
dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i]-fee)
dp[i][1] = max(dp[i-1][1] , dp[i-1][0]+prices[i])
细微变化在于多减去手续费

五、子序列问题

5.1子序列(不连续)

5.1.1最长上升子序列 ※

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

1、确定dp数组以及下标的含义
dp[i]:下标为i以内(包括i)找到的最长递增子序列长度为dp[i]
2、确定递推公式
if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
3、dp数组如何初始化
每一个i,对应的dp[i](即最长递增子序列)起始大小至少都是1.
4、遍历顺序
从左到右

5.1.2最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

1、确定dp数组以及下标的含义
dp[i][j]:以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j] 【i-1、j-1的原因就是为了初始化的时候方便】
2、确定递推公式
相等的情况下 nums[i-1] == nums[j-1]
dp[i][j] = dp[i-1][j-1]+1
不相等的情况下
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]) 【两边都可以进行删除】
3、dp数组如何初始化
dp[0][0] = 0;
4、遍历顺序
从左到右

5.1.3不相交的线【与5.1.2最长公共子序列几乎一样】

5.2子序列(连续)

5.2.1最长连续递增序列

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

1、确定dp数组以及下标的含义
dp[i]:下标为i以内(包括i)找到的最长连续递增的子序列长度为dp[i]
2、确定递推公式
if (nums[i] > nums[i-1]) dp[i] = dp[i-1] + 1);
3、dp数组如何初始化
每一个i,对应的dp[i]起始大小至少都是1.
4、遍历顺序
从左到右

5.2.2最长重复子数组

给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。

1、确定dp数组以及下标的含义
dp[i][j]:以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j] 【i-1、j-1的原因就是为了初始化的时候方便】
2、确定递推公式
相等 nums[i-1]==nums[j-1]
dp[i][j] = dp[i-1][j-1]+1
3、dp数组如何初始化
dp[0][0] = 0
4、遍历顺序
从左到右

5.2.3最大子序和 ※

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

1、确定dp数组以及下标的含义
dp[i]:包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]。
2、确定递推公式
dp[i]只有两个方向可以推出来:
dp[i - 1] + nums[i],即:nums[i]加入当前连续子序列和
nums[i],即:从头开始计算当前连续子序列和
3、dp数组如何初始化
dp[0] = nums[0]
4、遍历顺序
从左到右

5.3编辑距离

5.3.1判断子序列【不连续】

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

1、确定dp数组以及下标的含义
dp[i][j]:以下标i - 1为结尾的A是以下标j - 1为结尾的B的子序列,那么dp[i][j] = true
2、确定递推公式
相等的情况下 s[i-1] == t[j-1]
dp[i][j] = dp[i-1][j-1]+1
不相等的情况下
dp[i][j] = dp[i][j-1] 【只能删除t不能删除s】
3、dp数组如何初始化
dp[0][0] = 0;
4、遍历顺序
从左到右

5.3.2不同的子序列 【不连续----hard】※

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

1、确定dp数组以及下标的含义
dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
2、确定递推公式
s[i-1] == t[j-1]
dp[i][j] = dp[i-1][j-1] (使用s[i-1]这个元素) + dp[i-1][j](不使用s[i-1]这个元素 使用s[i-1]之前的元素)
那么如果不相等的话 else dp[i][j] = dp[i-1][j] 也就是不使用s[i-1]这个元素
3、dp数组如何初始化
dp[i][0] —> s[i-1] t[-1] -1相当于是空字符串,t是空字符号串 s不是空的,s中的元素全部删除之后我们的s就变成了空字符串,因此只有这一种方法使s变成t。dp[i][0] = 1;
dp[0][j] —> s[-1] t[j-1] s是空字符串 t不是空的,没有任何一种方法可以使s变成t,因此dp[0][j] =0;
这两个中间有一个交集 dp[0][0] —> s是空字符串 t是空字符串 有一种方法可以让s变成t。因此dp[0][0] = 1;
4、遍历顺序
从左到右

5.3.3两个字符串的删除操作

给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。

与上一题相比两个字符都可以删除

1、确定dp数组以及下标的含义
dp[i][j]:以i-1为结尾的word1子序列与以j-1为结尾的word2相同所需要的最小步数为dp[i][j]。
2、确定递推公式
相等的情况:
不操作dp[i][j] = dp[i-1][j-1]
不相等的情况:
可以删除word1也可以删除word2:
dp[i][j] = min(dp[i-1][j] , dp[i][j-1])+1
3、dp数组如何初始化
for(int i=0;i<word1.length();i++) dp[i][0] = i
for(int j=0;j<word2.length();i++) dp[0][j] = j
4、遍历顺序
从左到右

也可以求两个字符串的最长公共子序列是多少,然后最后两个字符串长度相加减去2倍的公共子序列长度即可 参考5.1.2最长公共子序列

5.3.4编辑距离

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符

1、确定dp数组以及下标的含义
dp[i][j]:以i-1为结尾的word1子序列与以j-1为结尾的word2相同所需要的最小步数为dp[i][j]。
2、确定递推公式
相等的情况:
不操作dp[i][j] = dp[i-1][j-1]
不相等的情况:
1、删除操作:可以删除word1也可以删除word2:dp[i][j] = min(dp[i-1][j] , dp[i][j-1])+1
2、插入操作:和删除操作为逆操作 因此递推公式一致
3、替换操作:dp[i-1][j-1]+1
因此,递推公式为:dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1] )+1
3、dp数组如何初始化
for(int i=0;i<word1.length();i++) dp[i][0] = i
for(int j=0;j<word2.length();i++) dp[0][j] = j
4、遍历顺序
从左到右

5.4回文

5.4.1回文子串【连续】

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

1、确定dp数组以及下标的含义
布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
2、确定递推公式
相等: s[i] == s[j]
情况一:i==j dp[i][j] = true “a”
情况二:j-1=1 dp[i][j] = true “aa”
情况三:j-i >1 dp[i][j] = dp[i+1][j-1];
不相等:s[i] !=s[j]
dp[i][j] = false
3、dp数组如何初始化
都为false
4、遍历顺序
从下到上 从左到右
一个true 结果res++ 最后直接return res

5.4.2最长回文子序列【不连续】

给定一个字符串 s ,找到其中最长的回文子序列,并返回该序列的长度。可以假设 s 的最大长度为 1000

1、确定dp数组以及下标的含义
布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)最长的回文子序列长度为dp[i][j]
2、确定递推公式
相等: s[i] == s[j]
dp[i][j] =dp[i+1][j-1]+2
不相等:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
3、dp数组如何初始化
对角线=1 dp[i][i] = 1
4、遍历顺序
从下到上 从左到右


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值