题目介绍
给你一个整数数组 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
思路解析
本题的一个难点在于子序列可以不连续,那么我们如何能够确定这个子序列呢?
动规五部曲
确定dp数组及其下标含义
dp[i]:表示以第i个元素为结尾的严格递增子序列(必须包括第i个)
递推公式
for (int j = 0; j < i; j++) { //[0,j]+i部分的区域j、i必取
if (nums[i] > nums[j])
dp[i] = Integer.max(dp[i], dp[j] + 1);
}
这里的关键之处,要遍历以下标j为结尾的前区间,如此循环遍历,最终即可得到以下标i结尾的最长严格子序列。
结果不是最后一位dp数值,所以我们需要记录过程中的最大值
初始化dp数组
Arrays.fill(dp,1);
每个位置保底为1,对应它本身
确定遍历顺序
正序遍历即可
打印dp数组检验
本题关键要确定dp数组及其下标含义,还要想到循环遍历得到结果,复杂度 O ( n 2 ) O(n^2) O(n2)
class Solution {
int result = 1;
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];//以第i个元素为结尾的最长严格递增子序列
Arrays.fill(dp,1);
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < i; j++) { //[0,j]+i部分的区域j、i必取
if (nums[i] > nums[j])
dp[i] = Integer.max(dp[i], dp[j] + 1);
}
result = Integer.max(dp[i], result);
}
return result;
}
}
题目介绍
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 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。
个人思路
本题相比于上一题简单不少,因为它有个连续的限制,我们不需要再遍历就可以直接确定dp数组的每个元素
直接上代码了
class Solution {
public int findLengthOfLCIS(int[] nums) {
int result = 1;
int[] dp = new int[nums.length];
dp[0] = 1;
for (int i = 1; i < nums.length; i++) {
dp[i] = nums[i] > nums[i - 1] ? dp[i - 1] + 1 : 1;
result = Integer.max(result, dp[i]);
}
return result;
}
}
题目介绍
给两个整数数组 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
个人思路
本题相较于前两题难点在于,这里给了两个数组,所以我们考虑使用二维dp数组来记录一些中间状态。注意到本题要求,连续子序列,所以这道题其实就是上一题的二维升级版
不难得出递推公式:dp[i][j] = nums1[i] == nums2[j] ? dp[i - 1][j - 1] + 1 : 0;
由此可以推出初始化操作,必须把0下标位置初始化好,避免越界问题
动规五部曲
确定dp数组及其下标含义
int[][] dp = new int[nums1.length][nums2.length];
dp[i][j]
表示下标[i]、[j]
位置为结尾的两子串符合条件的最大长度
递推公式的确定
dp[i][j] = nums1[i] == nums2[j] ? dp[i - 1][j - 1] + 1 : 0;
初始化dp数组
for (int i = 0; i < nums1.length; i++) {
dp[i][0] = nums1[i] == nums2[0] ? 1 : 0;
result = Integer.max(result, dp[i][0]);
}
for (int i = 0; i < nums2.length; i++) {
dp[0][i] = nums1[0] == nums2[i] ? 1 : 0;
result = Integer.max(result, dp[0][i]);
}
由递推公式推出
确定遍历顺序
两层for循环,正序遍历即可
打印dp数组检验
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int result = 0;
int[][] dp = new int[nums1.length][nums2.length];//以i.j为结尾的符合条件的子数组最长长度
for (int i = 0; i < nums1.length; i++) {
dp[i][0] = nums1[i] == nums2[0] ? 1 : 0;
result = Integer.max(result, dp[i][0]);
}
for (int i = 0; i < nums2.length; i++) {
dp[0][i] = nums1[0] == nums2[i] ? 1 : 0;
result = Integer.max(result, dp[0][i]);
}
for (int i = 1; i < nums1.length; i++) {
for (int j = 1; j < nums2.length; j++) {
dp[i][j] = nums1[i] == nums2[j] ? dp[i - 1][j - 1] + 1 : 0;
result = Integer.max(result, dp[i][j]);
}
}
return result;
}
}