给你一个整数数组 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
算法分析:
定义dp数组及下标含义:
dp[i]表示以nums[i]元素结尾的递增子序列长度。
递推公式:
if(nums[i]>nums[j])dp[i]=max(dp[i],dp[j]+1);
j从i-1倒叙遍历到0,如果元素j的值小于i那么dp[i]可以由2两个方向推导出来:
1、从第j个元素结尾的递增子序列长度加上1(i这个元素);
2、从原来的dp[i]推导。
我们取两个当中的最大值。
for(int i = 0; i < nums.length; i++)
dp[i]=1;
对于每个元素结尾的递增子序列长度至少为1(只有一个元素时,递增子序列长度也是1)。
从前往后遍历每一个元素,以该元素为子序列结尾,再遍历这个元素之前的元素。
代码如下:
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
for(int i = 0; i < nums.length; i++)
dp[i] = 1;//每个元素结尾的递增子序列至少是1.
int maxLen = 0;//记录最长的递增子序列长度
for(int i = 1; i < nums.length; i++) {
int j = i - 1;
while(j >= 0) {
if(nums[i] > nums[j]){
dp[i] = Math.max(dp[i], dp[j] + 1);
}
j--;
}
if(dp[i] > maxLen) maxLen = dp[i];
}
return maxLen;
}
}
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l
和 r
(l < r
)确定,如果对于每个 l <= i < r
,都有 nums[i] < nums[i + 1]
,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]]
就是连续递增子序列。
示例 1:
输入:nums = [1,3,5,4,7] 输出:3 解释:最长连续递增序列是 [1,3,5], 长度为3。 尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
示例 2:
输入:nums = [2,2,2,2,2] 输出:1 解释:最长连续递增序列是 [2], 长度为1。
提示:
1 <= nums.length <= 104
-109 <= nums[i] <= 109
算法分析:
dp[i]表示以元素i为结尾的连续递增子序列的长度。
if(nums[i]>nums[i-1])dp[i]=dp[i-1]+1;
else dp[i]=1;
如果第i个元素大于i-1的元素,那么从i-1到i连续递增,以i结尾的连虚递增子序列长度为dp[i]=dp[i-1]+1;
否则第i个元素结尾的连续递增子序列长度为1(只有第i个元素)。
从前往后遍历每一个元素。
代码如下:
class Solution {
public int findLengthOfLCIS(int[] nums) {
int[] dp = new int[nums.length];//以每个元素结尾的连续递增子序列长度
dp[0] = 1;
int maxLen = 1;
for(int i = 1; i < nums.length; i++) {
if(nums[i] > nums[i-1]){
dp[i] = dp[i-1]+1;
}else{
dp[i] = 1;
}
if(dp[i] > maxLen) maxLen = dp[i];
}
return maxLen;
}
}
给两个整数数组 nums1
和 nums2
,返回 两个数组中 公共的 、长度最长的子数组的长度 。
示例 1:
输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7] 输出:3 解释:长度最长的公共子数组是 [3,2,1] 。
示例 2:
输入:nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0] 输出:5
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 100
算法分析:
定义dp数组及下标含义:
dp[i][j],如果nums1第i个元素和nums2的第j个元素相等,那么dp[i][j]表示以该元素为结尾的重复子数组长度,两个元素如果不相等那么长度自然为0。
递推公式:
if(nums1[i] == nums2[j])dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=0;
如果nums1的第i个元素和nums2的第j个元素相等,那么以该元素结尾的重复子数组长度为它们前一个元素结尾的重复子数组长度加一,否则长度为0。
初始化:
由于dp[i][j]可能由dp[i-1][j-1]推到出来,所以要初始化左边那一列dp[i][0]和上边那一行dp[0][j]。
for(int i = 0; i < nums2.length; i++) {
if(nums1[0] == nums2[i])
dp[0][i] = 1;
else dp[0][i] = 0;
}
for(int i = 1; i < nums1.length; i++) {
if(nums1[i] == nums2[0])
dp[i][0] = 1;
else dp[i][0] = 0;
}
先遍历nums1,在遍历nums2或先遍历nums2再遍历nums1都可以。
代码如下:
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int[][] dp = new int[nums1.length][nums2.length];
int maxLen = 0;//记录重复子数组最长长度
for(int i = 0; i < nums2.length; i++) {
if(nums1[0] == nums2[i]){
dp[0][i] = 1;
maxLen = 1;
}
}
for(int i = 1; i < nums1.length; i++) {
if(nums1[i] == nums2[0]){
dp[i][0] = 1;
maxLen = 1;
}
}
for(int i = 1; i < nums1.length; i++) {
for(int j = 1; j < nums2.length; j++) {
if(nums1[i] == nums2[j]) {
dp[i][j] = dp[i-1][j-1]+1;
}
if(dp[i][j] > maxLen) maxLen = dp[i][j];
}
}
return maxLen;
}
}
这三道题只要熟练掌握了动规五部曲还是比较容易做出来的。
再来回顾一下动规五部曲:
1、定义dp数组和下标的含义。
2、递推公式。
3、初始化。
4、遍历顺序。
5、打印dp数组。