【代码随想录】动态规划复习

解题步骤

1. 确定dp数组及其下标含义

2. 确定递归顺序

3. dp数组如何初始化

4. 确定遍历顺序

5. 举例推导dp数组

经典题目

类型一  爬楼梯、不同路径

1. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

动规五部曲:

(1)dp[i]的含义:到达第i台阶所花费的最少体力为dp[i]。

(2)递推公式:到达第i个台阶两种选择,一种是从前1个台阶来的,另一种是从前2个台阶来的

从前1个台阶来:dp[i-1]+cost[i-1]

从前2个台阶来:dp[i-2]+cost[i-2]

dp[i]=min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]) 选取最小花费

(3)初始化:可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯,因此dp[0]=0,dp[1]=0

(4)遍历顺序:从前向后,从下标2开始

(5)举例推导dp数组

2. 不同路径Ⅱ

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

动规五部曲:

(1)dp[i][j]的含义:表示到达第i行第j列的方格有dp[i][j]条不同的路径

(2)递推公式:

如果遇到障碍物,dp[i][j]=0

否则,路径存在从上来或从左来两种,dp[i][j]=dp[i-1][j]+dp[i][j-1]

(3)初始化:第一行和第一列只存在一种路径,但如果有障碍物,障碍物后面的数值都为0

(4)遍历顺序:从上到下,从左到右

类型二  背包问题

1. 分割等和子集

只包含正整数 的 非空 数组 nums ,判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等

背包思路:

元素和相等 -> 背包容量为sum/2

数字大小 -> 物体重量、价值

数组能够分割为两个子集元素和相等 -> 找到总和为sum/2的子集

数字只能用一次 -> 背包的物体不可重复放入

动规五部曲:

(1)dp[i]的含义:表示容量为i的背包能够最大放入的元素和为dp[i]

(2)递推公式:dp[i]=max(dp[i], dp[i-n]+n)

(3)初始化:dp=[0]*(sum/2)

(4)遍历顺序:先物品后背包,背包倒序遍历

why?先背包:只能放入一个物品;背包正序:物品重复放入

(5)举例推导dp数组

2. 粉碎石头问题

背包思路:让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题,与分割等和子集的题目类似。不同点是分割等和子集问题是判断背包是否正好装满(dp[m]==m),本题是求背包最多能装多少((sum(stones)-dp[m])-dp[m])

3. 目标和

给定一个非负整数数组 nums 和一个整数 target 。向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 。返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目

解题思路:

通过加减nums的数,使得和等于target。在数组nums中,设加法之和为x,则减法有sum-x,那么x-(sum-x)=target,则x=(target+sum)//2

对于除号,如果非整除,则无法构造出等于target的表达式,方案数为0;如果给定的target本身大于nums之和,也无法构造出表达式,方案数为0。(特殊情况)

背包思路:

找出等于 target -> 找出加法之和为(target+sum)//2 -> 背包容量为(target+sum)//2

nums数组的数-> 物品重量、物品价值

nums的每个数只能用一次 -> 物品不可重复使用 (01背包)

动规五部曲:

(1)dp[i]的含义:表示目标加法和为i的方案数为dp[i]

(2)递推公式:方案数 -> 组合数  在存在nums[i]的情况下,凑成dp[j]就有dp[j - nums[i]] 种方法

那么所有方案数就是列举存在每种物品下,每种方案数的累加和,即:

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

(3)初始化:因为要累加,所以dp[0]=1

(4)遍历顺序:先物品后背包,背包倒序

(5)举例推导dp数组

**如果求组合数就是外层for循环遍历物品,内层for遍历背包

**如果求排列数就是外层for遍历背包,内层for循环遍历物品

4. 一和零

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

背包思路:题目求解最大子集长度-> 背包最大价值问题

strs数组的元素 -> 物品

m和n -> 二维背包

0和1的数量 -> 物品重量

字符串个数 -> 物品价值

动规五部曲:

(1)dp[i][j]的含义:表示含有i个0和j个1的最大子集长度为dp[i][j]

(2)递推公式:dp[i][j]=max(dp[i][j], dp[i-zeronum][j-onenum]+1)

(3)初始化:物品价值不会是负数,初始为0

(4)遍历顺序:先物体后背包,背包倒序,背包的m和n遍历顺序无要求

(5)举例推导dp数组

5. 完全平方数

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

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

背包思路:

背包容量->正整数n

物品->完全平方数(可以重复使用,完全背包)

物品重量->完全平方数的大小

物品价值->完全平方数本身的个数

动规五部曲:

(1)dp[i]的含义:和为n的完全平方数的最少数量为dp[i]

(2)递推公式:dp[i]=min(dp[i], dp[i-nums[i]]+1)

(3)初始化:最小值,初始化为 float('inf'),dp[0]=0

(4)遍历顺序:先物品后背包/先背包后物品都可以

6. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

背包思路:

物品->单词

背包->字符串s

完全背包问题->拆分时可以重复使用字典中的单词

动规五部曲:

(1)dp[i]的含义:字符串长度为i时,能够否拆分为一个或多个在字典中出现的单词true/false

(2)递推公式:dp[j]=true,且[j,i]这个区间的子串出现在字典里,则dp[i]=true

(3)初始化:dp[0]=true

(4)遍历顺序:排列数:先背包后物品

(5)举例推导dp数组

背包问题总结

递推公式

1. 问能否装满背包或最多装多少?dp[j]=max(dp[j], dp[j-nums[j]]+nums[j])

2. 问装满背包有几种方法? dp[j]+=dp[j-nums[i]]

3. 问背包装满的最大价值? dp[j]=max(dp[j], dp[j-weight[j]]+value[i])

4. 问装满背包所有物品的最小个数? dp[j]=min(dp[j-coins[i]]+1,dp[j])

遍历顺序

01背包:二维dp数组两种都可以,第二层是从小到大遍历;一维dp数组只能先物品后背包,背包倒序遍历

完全背包:纯完全背包问题:一维dp数组两种都可以,第二次是从小到大遍历;求组合数:先物品后背包;求排列数:先背包后物品;求最小数:两种都可以

类型三  打劫问题

1. 题一 

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。

解题思路:

成环有三种情况:①不考虑尾元素;②不考虑首元素;③不考虑首尾元素。情况①②包含情况③

动规五部曲:

(1)dp[i]的含义:表示是考虑下标i(包括i)范围内的房子,能够偷窃的最高金额为dp[i]

(2)递推公式:dp[i]=max(dp[i-2]+nums[i], dp[i-1] )

对于房子i,偷:dp[i-2]+nums[i] (说明第i-1间房子没偷)

对于房子i,不偷:dp[i-1]

(3)初始化:dp[0]=nums[0];dp[1]=max(nums[0], nums[1])

(4)遍历顺序:dp依赖于i-2和i-1,因此从前向后遍历

(5)举例推导dp数组

2. 树形DP

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。

除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

递归三部曲+动规五部曲:

(1)确定递归函数的参数和返回值

参数:当且节点

返回值:当前节点偷与不偷两个状态得到的金钱,长度为2的dp数组

(2)终止条件:空节点,偷与不偷都是0(相当于dp数组的初始化)

(3)遍历顺序:后序遍历(左右中)递归左节点,得到左节点偷与不偷的金钱;递归右节点,得到右节点偷与不偷的金钱

(4)单层递归逻辑:

不偷当前节点,则偷左右孩子,选一个最大的:val2=max(left[0],left[1])+max(right[0],right[1])

偷当前节点,左右孩子不能偷:val1=cur.val+left[0]+right[0]

最后当前节点的状态为 {val2,val1},即{不偷当前节点得到的最大金钱,偷当前节点得到的最大金钱}

类型四  买卖股票问题

1. 一只股票只能买卖一次

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 

动规五部曲:

(1)dp[i][0]的含义:表示第i天持有股票所得的最多现金;dp[i][1]的含义:表示dii天不持有股票所得的最多现金

(2)递推公式

dp[i][0]:保持现状:dp[i-1][0],当天买入:-prices[i],选最大值:dp[i][0]=max(dp[i-1][0],-prices[i])

dp[i][1]:保持现状:dp[i-1][1],当天卖出:dp[i-1][0]+prices[i],选最大值:dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])

(3)初始化:dp[0][0]=-prices[0],dp[0][1]=0

(4)遍历顺序:从前到后

(5)举例推导dp数组

2. 一只股票可以买卖多次

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 

动规五部曲:

(1)dp[i][0]的含义:表示第i天持有股票所得的最大现金;dp[i][1]的含义:表示第i天不持有股票所得的最大现金

(2)递推公式:

dp[i][0]:保持现状,即dp[i-1][0];当天买入股票,即dp[i-1][1]-prices[i],取两者最大值

dp[i][1]:保持现状,即dp[i-1][1];当天卖出股票,即dp[i-1][0]+prices[i],取两者最大值

(3)初始化:dp[0][0]=-prices[0];dp[0][1]=0

(4)遍历顺序:从前向后

(5)举例推导dp数组

**与题1的区别在于本题可以买卖多次,所以当天买入股票是用前一天不持有股票的现金减去当天股票价格,而题1只能买卖一次,当天买入股票则是-prices[i]

3. 一只股票买卖K次

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

动规五部曲:

(1)dp[i][j]的含义:表示第i天状态为j,所得的最大现金为dp[i][j]

j的状态表示为:0不操作 1第一次买入 2第一次卖出 3第二次买入 4第二次卖出···

除0以外,偶数买入,奇数卖出

(2)递推公式:

j为偶数,有保持现状和当天买入两种情况:dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]-prices[i])

j为奇数,有保持现状和当天卖出两种情况:dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+prices[i])

(3)初始化:偶数买入,初始化为-prices[0],奇数初始化为0

(4)遍历顺序:从前向后,依赖于i-1

(5)举例推导dp数组

4. 买卖包含冷冻期

三个操作:买入、卖出、冷冻

四个状态:买入股票(0)、保持卖出状态(1)、当天卖出(2)、冷冻期(3)

why?本题有冷冻期,而冷冻期的前一天,只能是 「今天卖出股票」状态,如果是 「不持有股票状态」那么就很模糊,因为不一定是 卖出股票的操作

动规五部曲:

(1)dp[i][j]的含义:表示第i天处于状态j所得的最大金额为dp[i][j]

dp[i][0]:第i天买入股票所得的最大金额

dp[i][1]:第i天保持卖出股票状态所得的最大金额

dp[i][2]:第i天卖出股票所得的最大金额

dp[i][3]:第i天处于冷冻期所得的最大金额

(2)递推公式

dp[i][0]=max(dp[i-1][0],max(dp[i-1][1],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]

(3)初始化:只有dp[0][0]=-prices[0],其他初始化为0

(4)递推顺序:从前向后

(5)举例推导dp数组

类型五  子序列问题

1. 最长递增子序列

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

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

动规五部曲:

(1)dp[i]的含义:表示i之前包括i的以nums[i]为结尾的最长递增子序列的长度

(2)递推公式/状态转移方程

位置i的最长递增子序列等于j从0到i-1各个位置的最长递增子序列+1的最大值

if nums[i]>nums[j]: dp[i]=max(dp[i],dp[j]+1)

因为j是遍历0到i-1得来的,得到很多个dp[i],因此需要取最大值

(3)初始化:对于每一个i,对应的dp[i](即最长递增子序列)起始大小至少为1

(4)遍历顺序:从前向后

(5)举例推导dp数组

2. 最长连续递增子序列

动规五部曲:

(1)dp[i]的含义:表示以下标i为结尾(并非以0起始)的连续递增子序列的长度为dp[i]

(2)递推公式:if nums[i]>nums[i-1]: dp[i]=dp[i-1]+1

(3)初始化:以下标i为结尾的连续递增的子序列长度至少为1

(4)遍历顺序:从前向后

(5)举例推导dp数组

**与题1的区别:不连续递增子序列的跟前0-i 个状态有关,连续递增的子序列只跟前一个状态有关

3. 最长重复子数组(实际为连续子序列)

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 

动规五部曲:

(1)dp数组的含义:dp[i][j]表示以下标i-1为结尾的A和以下标j-1为结尾的B,最长重复子数组长度为dp[i][j]  (遍历时i,j从1开始)

(2)递推公式:if A[i-1]==B[j-1]: dp[i][j]=dp[i-1][j-1]+1

(3)初始化:dp[i][0] 和dp[0][j]没有意义,dp[i][0] 和dp[0][j]初始化为0

(4)遍历顺序:先A后B或先B后A都行

(5)举例推导dp数组

4. 最长公共子序列/不相交的线

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

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

动规五部曲:

(1)dp[i][j]的含义:表示长度为[0,i-1]的字符串text1和长度为[0,j-1]的字符串text2的最长公共子序列为dp[i][j]

(2)递推公式

text1[i-1]==text2[j-1]:找到公共元素,dp[i][j]=dp[i-1][j-1]+1

text1[i-1] != text2[j-1]:dp[i][j]=max(dp[i-1][j], dp[i][j-1])

(3)初始化:0

(4)遍历顺序:从前向后

(5)举例推导dp数组

5. 最大子序和

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

动规五部曲:

(1)dp[i]的含义:表示包括下标i(以nums[i]为结尾)的最大连续子序列和为dp[i]

(2)递推公式:dp[i]=max(dp[i-1]+nums[i], nums[i])

dp[i-1]+nums[i]:当前数字加入连续子序列和

nums[i]:重新计算当前连续子序列和

(3)初始化:dp[i]依赖于dp[i-1],dp[0]=nums[0]

(4)遍历顺序:从前向后

(5)举例推导dp数组

6. 乘积最大子数组

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

测试用例的答案是一个 32-位 整数。

解题思路

因为可能存在负数,负数x负数则为正,可能乘积更大,负数x正数则为负,乘积变小,因此需要记录最大值、最小值,然后比较乘积哪个更大

代码

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        if not nums: return 
        res=nums[0]
        pre_max=nums[0]
        pre_min=nums[0]
        for i in range(1,len(nums)):
            #记录最大值
            cur_max=max(pre_max*nums[i],pre_min*nums[i],nums[i])
            cur_min=min(pre_min*nums[i],pre_max*nums[i],nums[i])
            res=max(res,cur_max)
            pre_max=cur_max
            pre_min=cur_min
        return res

类型六  编辑距离

1. 判断子序列

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

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

动规五部曲:(类似于最长公共子序列)

(1)dp[i][j]的含义:以下标i-1为结尾的字符串s和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]

(2)递推公式

t中找到了一个在s中也出现的字符  if s[i-1]==t[j-1]:  dp[i][j]=dp[i-1][j-1]+1

s的当前字符与t的当前字符不匹配  if s[i-1] != t[j-1] : dp[i][j]=dp[i][j-1]

**最长公共子序列的两个字符串都可以删元素,而本题只能删字符串t

(3)初始化:初始化为0

(4)遍历顺序:从前到后

(5)举例推导dp数组

**dp[i][j]以下标i-1为结尾的字符串s和以下标j-1为结尾的字符串t 相同子序列的长度,如果dp[s.size()][t.size()] 与 字符串s的长度相同说明,s与t的最长相同子序列就是s,那么s 就是 t 的子序列。

2. 不同的子序列

给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 109 + 7 取模。

动规五部曲:

(1)dp[i][j]:表示以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]

(2)递推公式:

s[i-1]==t[j-1]:一部分用s[i-1]匹配,不需要考虑当前s子串和t子串的最后一位字母(对个数没有影响),个数为dp[i-1][j-1];另一部分不用s[i-1]匹配,个数为dp[i-1][j]。dp[i][j]=dp[i-1][j-1]+dp[i-1][j]

why?s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag,当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。

s[i-1]!=t[j-1]:不用s[i - 1]来匹配,在s中删除元素,即dp[i][j] = dp[i-1][j]

(3)初始化:dp[i][0] 表示:以i-1为结尾的s可以随便删除元素,出现空字符串的个数,dp[i][0]=1

dp[0][j]:空字符串s可以随便删除元素,出现以j-1为结尾的字符串t的个数,dp[0][j]=0

dp[0][0]=1,空字符串s,可以删除0个元素,变成空字符串t。

3. 编辑距离

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

动规五部曲:

(1)dp[i][j]的含义:表示以i-1为结尾的word1转换成以j-1为结尾的word2使用的最少操作数(word1转换为word2和word2转换为word1是等价的)

(2)递推公式:dp[i][j]=min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+1)

①如果word1[i-1]==word2[j-1],不需要操作,那么延续之前的,即dp[i-1][j-1]

②如果word1[i-1] !=word2[j-1],则需要增、删、替换

增:word1增加,即dp[i-1][j]+1;word2增加,即dp[i][j-1]+1

删:word1删相当于word2增,增和删是等价的,所以相同

替换:word1[i-1]==word2[j-1],等于dp[i-1][j-1],那么不相等替换时,则为dp[i-1][j-1]+1

(3)初始化:

dp[i][0]=i, dp[0][j]=j

(4)遍历顺序:从上到下,从左到右

(5)举例推导dp数组

类型七  回文子串

1. 回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

解题思路:判断一个子字符串(字符串的下表范围[i,j])是否回文,依赖于,子字符串(下表范围[i + 1, j - 1])) 是否是回文。如果s[i]==s[j],且子字符串[i+1,j-1]是回文,呢吗该字符串为回文

动规五部曲:

(1)二维dp[i][j]的含义:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。

(2)递推公式:

如果s[i] != s[j]:dp[i][j]=False

如果s[i]==s[j]:下标i与j相同(a),是回文子串;下标i与j相差1(aa),是回文子串;下标i与j相差大于1(cabac),看i到j区间的回文子串是不是回文,即dp[i+1][j-1]是否为True

(3)初始化:False

(4)遍历顺序:从下到上,从左到右,因为dp[i+1][j-1]

2. 最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

动规五部曲:

(1)dp[i][j]的含义:表示区间[i,j]的字符串的回文子序列最长为dp[i][j]

(2)递推公式

如果s[i]==s[j]:回文子串[i+1,j-1]基础上长度加2,即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])

(3)初始化:递推公式没有包含i=j的情况,因此当i=j,只有一个元素,此时回文长度为1

(4)遍历顺序:从递推公式可知,遍历从下到上,从左到右

(5)举例推导dp数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值