• 第九章 动态规划 part13 300. 最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组


    第五十五天| 第九章 动态规划 part13 300. 最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组

    一、300. 最长递增子序列

    • 题目链接:https://leetcode.cn/problems/longest-increasing-subsequence/

    • 题目介绍:

      • 给你一个整数数组 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 。
        
        • 1
        • 2
        • 3
    • 思路:

      • (1)确定dp数组及下标含义:

        • dp[i]:表示的是以nums[i]结尾的最长递增子序列的长度
          
          • 1
      • (2)确定递推公式:

        • 递推公式:对比nums[i]和以下标从0到i-1的nums[j],如果nums[i] > nums[j],那么dp[i] = Math.max(dp[j] + 1, dp[i])。这里和dp[i]求最大值目的是求出从下标0到i-1的所有的dp[j] + 1的最大值。
          
          • 1
      • (3)初始化dp数组:

        • 根据dp数组的含义,dp数组的每一个值初始化为1,因为最起码当前这个值可以作为递增子序列。
          
          • 1
      • (4)确定遍历顺序:

        • 正序
    • 代码:

    class Solution {
        public int lengthOfLIS(int[] nums) {
            if (nums == null || nums.length == 0) return 0;
            // (1)确定dp数组及下标含义:
            // dp[i]:表示的是以nums[i]结尾的最长递增子序列的长度
            int[] dp = new int[nums.length];
            // (3)初始化dp数组
            // 根据dp数组的含义,dp数组的每一个值初始化为1,因为最起码当前这个值可以作为递增子序列。
            Arrays.fill(dp, 1);
            // (4)确定遍历顺序:
            // 后面的值是由前面确定的,因此是正序遍历
            int result = 1;
            for (int i = 1; i < nums.length; i++) {
                // (2)确定递推公式:
            	// 递推公式:对比nums[i]和以下标从0到i-1的nums[j],如果nums[i] > nums[j],那么dp[i] = Math.max(dp[j] + 1, dp[i])。这里和dp[i]求最大值目的是求出从下标0到i-1的所有的dp[j] + 1的最大值。
                for (int j = 0; j < i; j++) {
                    if (nums[i] > nums[j]) dp[i] = Math.max(dp[j] + 1, dp[i]);
                }
                // 注意:惯性思维,在dp问题中,我们会认为最终最优结果是dp[nums.length - 1],但本题不是。根据dp数组的含义,我们要找的最终结果是dp数组中最大的那个值。
                if (dp[i] > result) result = dp[i];
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    二、674. 最长连续递增序列

    • 题目链接:https://leetcode.cn/problems/longest-continuous-increasing-subsequence/

    • 题目介绍:

      • 给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

        连续递增的子序列 可以由两个下标 lrl < 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 隔开。 
        
        • 1
        • 2
        • 3
        • 4
    • 思路:

      • (1)确定dp数组及下标含义:

        • dp[i]:表示的是以nums[i]为结尾的最长连续递增序列
          
          • 1
      • (2)确定递推公式:

        • 与上一题不同的地方在于,本题要求的必须是连续的,因此dp[i]只能由dp[i-1] + 1得出
          
          • 1
      • (3)初始化dp数组:

        • 仍然全部初始化为1
          
          • 1
      • (4)确定遍历顺序:

        • 正序
    • 代码:

    class Solution {
        public int findLengthOfLCIS(int[] nums) {
            if (nums == null || nums.length == 0) return 0;
            // (1)确定dp数组及下标含义:
            // dp[i]:表示的是以nums[i]为结尾的最长连续递增序列
            int[] dp = new int[nums.length];
            // (3)初始化dp数组:
            // 仍然全部初始化为1
            Arrays.fill(dp, 1);
            int result = 1;
            // (4)确定遍历顺序:
            // 正序遍历
            for (int i = 1; i < nums.length; i++) {
                // (2)确定递推公式:
                // 与上一题不同的地方在于,本题要求的必须是连续的,因此dp[i]只能由dp[i-1] + 1得出
                if (nums[i] > nums[i-1]) dp[i] = dp[i-1] + 1;
                if (dp[i] > result) result = dp[i];
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    三、718. 最长重复子数组

    • 题目链接:https://leetcode.cn/problems/maximum-length-of-repeated-subarray/

    • 题目介绍:

      • 给两个整数数组 nums1nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度

        示例 1:

        输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
        输出:3
        解释:长度最长的公共子数组是 [3,2,1] 。
        
        • 1
        • 2
        • 3
    • 思路:

      • (1)确定dp数组及下标的含义:

        • dp[i][j]:表示的是以下标i-1为结尾的nums1和以下标j-1为结尾的nums2的最长重复子数组的长度。
          
          • 1
        • 为什么要定义为i-1和j-1呢?是因为初始化的时候便于初始化,在初始化的部分再详细解释

      • (2)确定递推公式:

        • 因为求得的是重复子数组的最大长度,因此dp[i][j]应该由nums1的i-1位和nums2的i-2位推导出来,即二维数组的左斜上角
        • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • (3)dp数组初始化

        • 全部初始化为0,这里主要强调的是dp[i][0]和dp[0][j]这一列一行,这一列一行根据dp数组的含义是没有任何意义的,所以初始化为0。但如果本题dp数组含义改为...i...j,那么第一列和第一行就要根据是否重复设定为1,会复杂一些。因此定义dp数组的含义为...i-1...j-1
          
          • 1
      • (4)遍历顺序:

        • 两层for循环,从下标1开始,正序遍历
    • 代码:

    class Solution {
        public int findLength(int[] nums1, int[] nums2) {
            int result = 0;
            // (1)确定dp数组及下标的含义:
            // dp[i][j]:表示的是以下标i-1为结尾的nums1和以下标j-1为结尾的nums2的最长重复子数组的长度。
            // 为什么要定义为i-1和j-1呢?是因为初始化的时候便于初始化,在初始化的部分再详细解释
            int[][] dp = new int[nums1.length + 1][nums2.length + 1];
            // (3)dp数组初始化:
            // 全部初始化为0,这里主要强调的是dp[i][0]和dp[0][j]这一列一行,这一列一行根据dp数组的含义是没有任何意义的,所以初始化为0。但如果本题dp数组含义改为...i...j,那么第一列和第一行就要根据是否重复设定为1,会复杂一些。因此定义dp数组的含义为...i-1...j-1
            // (4)遍历顺序:
            // 两层for循环,从下标1开始,正序遍历
            for (int i = 1; i <= nums1.length; i++) {
                for (int j = 1; j <= nums2.length; j++) {
                    // (2)确定递推公式:
            		// 因为求得的是重复子数组的最大长度,因此dp[i][j]应该由nums1的i-1位和nums2的i-2位推导出来,即二维数组的左斜上角
                    if (nums1[i-1] == nums2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
                    if (dp[i][j] > result) result = dp[i][j];
                }
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    flutter_学习记录_02底部 Tab 切换保持页面状态的几种方法
    three.js 入门 初识
    (数据科学学习手札152)geopandas 0.13版本更新内容一览
    docker&kubernets篇(二十八)
    配置Swagger2生成API接口文档
    BI佐罗,居然抄袭洗稿我的文章
    优化算法|MOAVOA:一种新的多目标人工秃鹰优化算法(Matlab代码实现)
    【Linux】线程同步{死锁/线程同步相关接口/由浅入深理解线程同步}
    C++ 内存模型
    【牛客刷题】bfs和dfs (二叉树层序遍历、矩阵最长递增路径、被围绕的区域)
  • 原文地址:https://blog.csdn.net/qq_45498567/article/details/133496687