近期做十四届蓝桥杯真题,遇到好多类似子序列的题目,发现还是不会做,于是乎回来再做一遍代码随想录相关题目,回来总结一下这几道题子序列问题(300. 最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组)。接下来的总结,先给出代码,代码中有详细注释,根据代码随想录中的动态规划五部曲,对每一步做出自己的理解,最后来个总的汇总。
300.最长递增子序列
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7]
是数组 [0,3,1,6,2,2,7]
的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3] 输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7] 输出:1
提示:
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
1. dp数组的定义
关于dp数组的定义,首先应该确定维度
确定dp维度
一般根据所给数据的维度来确定dp的维度。那这道题明显dp为1维。这毕竟是普遍的规律,当然,其实还得看题目问什么。那到底怎么看呢?这里我总结的规律是:
先分析问题,将问题简单化,然后抓住所给数据的某个数,对它进行提问,去思考解决刚刚那个简单的问题:“它跟哪几个维度的数据匹配能得到(简单问题的)答案”。有几维的数据,就有几维dp。
有点抽象,例如本题,简单化,其实困扰我们的无非就是如何找递增的子序列长度,最大的问题可以先不管,拿样例1中的nums[3](数字5)进行提问:5与谁匹配能成为递增的子序列?是不是要么跟5前面小于5的数匹配成为一个递增的子序列,要么跟5后面小于5的数匹配成为一个递增的子序列(往往不往后匹配),那不管往前往后,都还是与5同一个维度吧,所以dp只需要一维就足够了。
确定dp[i]的定义
上面还只是确定维度,那dp[i]如何定义呢?根据一般子序列的规律,都是:
“以xxx结尾的,最大的xxx。”都是以某几个数字结尾的xxx
那到底应该怎么定义的呢?我的理解是(尽力解释了,这个东西是真的抽象):其实在确定维度的时候,就已经把dp[i]的计算方法确定了:就是定住nums[i]去比较它前面的第j个(j < i)是否有小于它,小于它则dp[i]等于dp[j] + 1。即dp[i]定义为:dp[i]表示的就是 0~i中,以nums[i]结尾的最大子序列长度。
2. 递推公式的确定
其实在上面思考dp应该如何定义的时候,