https://leetcode-cn.com/problems/longest-increasing-subsequence/
给你一个整数数组 nums
,找到其中最长严格递增子序列的长度。
var lengthOfLIS = function(nums) {
let n= nums.length
if(n<2){
return n
}
let max=1
let dp=new Array()
dp[0]=1
for(let i=0;i<n;i++){
dp[i]=1
for(let j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=Math.max(dp[i],dp[j]+1)
}
}
max=Math.max(max,dp[i])
}
return max
}
这道题是很经典「动态规划」算法问题。
需要对「子序列」和「子串」这两个概念进行区分;
子序列(subsequence):子序列并不要求连续,例如:序列 [4, 6, 5] 是 [1, 2, 4, 3, 7, 6, 5] 的一个子序列;
子串(substring、subarray):子串一定是原始字符串的连续子串。
题目中的「上升」的意思是「严格上升」。反例: [1, 2, 2, 3] 不能算作「上升子序列」;
首先考虑题目问什么,就把什么定义成状态。题目问最长上升子序列的长度,其实可以把「子序列的长度」定义成状态,但是发现「状态转移」不好做。
基于「动态规划」的状态设计需要满足「无后效性」的设计思想,可以将状态定义为「以 nums[i] 结尾 的「上升子序列」的长度」。
「无后效性」的设计思想:让不确定的因素确定下来,以保证求解的过程形成一个逻辑上的有向无环图。这题不确定的因素是某个元素是否被选中,而我们设计状态的时候,让 nums[i] 必需被选中,这一点是「让不确定的因素确定下来」,也是我们这样设计状态的原因。
1. 定义状态:
dp[i] 表示:以 nums[i] 结尾 的「上升子序列」的长度。注意:这个定义中 nums[i] 必须被选取,且必须是这个子序列的最后一个元素;
2. 状态转移方程:
如果一个较大的数接在较小的数后面,就会形成一个更长的子序列。只要 nums[i] 严格大于在它位置之前的某个数,那么 nums[i] 就可以接在这个数后面形成一个更长的上升子序列。
3. 初始化:
dp[i] = 1
,11 个字符显然是长度为 11 的上升子序列。