300.最长递增子序列
文章讲解:https://programmercarl.com/0300.%E6%9C%80%E9%95%BF%E4%B8%8A%E5%8D%87%E5%AD%90%E5%BA%8F%E5%88%97.html
题目链接:https://leetcode.cn/problems/longest-increasing-subsequence/
视频讲解:https://www.bilibili.com/video/BV1ng411J7xP/
没想到好的处理方式。
列出一张图,如 10 9 2 5 3 7 21这个序列要求最长递增子序列。
这道题还是相当难理解,后面手写推导了一次状态转移的逻辑就懂了。
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp,1);
int result = dp[0];
for(int i = 1; i < nums.length; i++){
for(int j = 0; j < i; j++){
if(nums[i] > nums[j]){
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
result = Math.max(result,dp[i]);
}
return result;
}
674. 最长连续递增序列
文章讲解:https://programmercarl.com/0674.%E6%9C%80%E9%95%BF%E8%BF%9E%E7%BB%AD%E9%80%92%E5%A2%9E%E5%BA%8F%E5%88%97.html
题目链接:https://leetcode.cn/problems/longest-continuous-increasing-subsequence/
视频讲解:https://www.bilibili.com/video/BV1bD4y1778v/
该题和上一题的区别就是本题是连续的。上一题不需要连续。连续和不连续的最大区别就是i只和小一位的j比较,如果比较失败直接为1。
自己写的代码,写完为了验证,还手动推导了一下:
public int findLengthOfLCIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp,1);
int result = dp[0];
for(int i = 1; i < nums.length; i++){
if(nums[i] > nums[i - 1]){
dp[i] = dp[i - 1] + 1;
}
result = Math.max(result,dp[i]);
}
return result;
}
这里和我想的一样,只需要比较nums[i]与nums[i - 1],而不用去比较nums[j]与nums[i] (j是在0到i之间遍历)。
既然不用j了,那么也不用两层for循环,一层for循环就行,比较nums[i] 和 nums[i - 1]。
无
718. 最长重复子数组
文章讲解:https://programmercarl.com/0718.%E6%9C%80%E9%95%BF%E9%87%8D%E5%A4%8D%E5%AD%90%E6%95%B0%E7%BB%84.html
题目链接:https://leetcode.cn/problems/maximum-length-of-repeated-subarray/
视频讲解:https://www.bilibili.com/video/BV178411H7hV/
暴力解法:
两层循环,外层是长度长一点的数组。然后循环遍历nums1,从nums2找到匹配的位置,匹配到了后一起开始向后移动,得到最大值。
动态规划,核心找到递推公式,动态规划五步骤:
dp数组定义:dp[i][j] i表示nums1[i]之前,j表示nums2[j]之前长度最长的子数组。dp[nums1.length -1][nums2.length - 1]就为子数组最长。
递推公式:动态规划没想出来
动态规划数组定义:
dp数组的初始化大小以及循环遍历的次数没处理好,循环的终止条件应该为i <= nums1.length、j <= nums2.length,因为第0位是无意义的值,且比较的是**nums1[i - 1] == nums2[j - 1]**实际要取到nums1.length的位置才能遍历完所有的值。
public int findLength(int[] nums1, int[] nums2) {
int[][] dp = new int[nums1.length + 1][nums2.length + 1];
// dp数组定义 dp[i][j]:用nums1的i-1位置为结尾以及nums2的j-1为结尾的最长子数组长度
int result = 0;
for(int i = 1; i <= nums1.length; i++){
for(int j = 1; j <= nums2.length; j++){
// 推导公式,如果每个数字的位数都相等,则长度加一
if(nums1[i - 1] == nums2[j - 1]){
dp[i][j] = dp[i - 1][j - 1] + 1;
result = Math.max(dp[i][j],result);
}
}
}
return result;
}
子序列问题这三题的关键是确定dp数组的定义,这个非常关键。
第一题最长递增子序列,dp数组的定义使用以nums[i]为结尾的最长连续子序列的长度来表示。然后再用j来表示0-i-1的最长子序列,若nums[i] > nums[j],则dp[i] = dp[j] + 1;
第二题最长连续递增子序列和第一题差不多,但是不需要做2次循环,只需要做一次循环然后和i-1比较即可。
第三题最长重复子数组,这道题的核心也是确定dp数组定义,和上面题目差不多,都是用取nums[i],取nums[j]为结尾做dp数组定义。然后再根据题目要义,连续子数组来做匹配。
整体耗时较长,但是都理解了题目。后续二刷