代码随想录算法训练营第五十二天|300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组
最长递增子序列
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这个序列要求最长递增子序列。
- 确定dp数组以及其下标含义
- dp[i]:以nums[i]为结尾的最长连续子序列的长度。
- 确定递推公式
- 简单来说就是定义一个i和j,j从0遍历到i-1,然后dp[i]的最大值由j遍历从0到i-1的dp[j]最大值得到,如果nums[i] > nums[j],那dp[i]的最大值就是此刻遍历到的j的dp[j]+1。
- 确定初始化值
- 因为每个位置至少有一个自己的数字作为最大值,因此所有的dp[i]的初始值都为1
- 确定遍历顺序
- 因为大的值都是由前面小的值推导过来的,因此遍历顺序应该是从前到后
- 打印数组
自己实现过程中遇到哪些困难
这道题还是相当难理解,后面手写推导了一次状态转移的逻辑就懂了。
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][j]表示以i-1为尾的nums1和以j-1为尾的num2的最长重复子数组的长度。
- 递推公式if(nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
- 初始化值,如果这里用dp[i][j]用以i结尾的nums1和以j为尾的nums2的最长重复子数组的话就需要做两次遍历取dp[0][j],dp[i][0]。而这里用i-1,j-1则免去了计算初始值的流程,因为当i=0、j=0时,i-1,j-1无意义,所以直接初始化成0
自己实现过程中遇到哪些困难
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数组定义。然后再根据题目要义,连续子数组来做匹配。
整体耗时较长,但是都理解了题目。后续二刷