zoukankan      html  css  js  c++  java
  • 力扣300题、674题、718题、1143题、1035题(子序列问题)

    300、最长递增子序列

    基本思想:

    动态规划

    具体实现:

    1.dp[i]的定义

    dp[i]表示i之前包括i的最长上升子序列的长度。

    2.状态转移方程

    位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 + 1 的最大值。

    if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

    3.dp[i]的初始化

    每一个i,对应的dp[i](即最长上升子序列)起始大小至少都是1.

    4.确定遍历顺序

    dp[i] 是有0到i-1各个位置的最长升序子序列 推导而来,那么遍历i一定是从前向后遍历。

    j其实就是0到i-1,遍历i的循环里外层,遍历j则在内层,代码如下:

    for (int i = 1; i < nums.length; i++) {
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
        }
        if (dp[i] > result) result = dp[i]; // 取长的子序列
    }

    5.举例推导

    代码:

    class Solution {
        public int lengthOfLIS(int[] nums) {
            int[] dp = new int[nums.length];
            Arrays.fill(dp, 1);
            for (int i = 0; i < dp.length; i++) {
                for (int j = 0; j < i; j++){
                    if (nums[i] > nums[j]){
                        dp[i] = Math.max(dp[i], dp[j] + 1);
                    }
                }
            }
            int res = 0;
            for (int i = 0; i < dp.length; i++){
                res = Math.max(res, dp[i]);
            }
            return res;
        }
    }

    二分搜索法:p101

    674、最长连续递增序列

    基本思想:

    上题的基础上加上连续的条件

    具体实现:

    1.确定dp数组(dp table)以及下标的含义

    dp[i]:以下标i为结尾的数组的连续递增的子序列长度为dp[i]。

    一定是以下标i为结尾,并不是说一定以下标0为起始位置。

    2.确定递推公式

    如果 nums[i + 1] > nums[i],那么以 i+1 为结尾的数组的连续递增的子序列长度 一定等于 以i为结尾的数组的连续递增的子序列长度 + 1 。

    即:dp[i + 1] = dp[i] + 1;

    注意与上一题的区别,本题要求的是连续递增子序列

    所以就必要比较nums[i + 1]与nums[i],而不用去比较nums[j]与nums[i] (j是在0到i之间遍历)。

    既然不用j了,那么也不用两层for循环,本题一层for循环就行,比较nums[i + 1] 和 nums[i]。

    3.dp数组如何初始化

    以下标i为结尾的数组的连续递增的子序列长度最少也应该是1,即就是nums[i]这一个元素。

    所以dp[i]应该初始1;

    4.确定遍历顺序

    从递推公式上可以看出, dp[i + 1]依赖dp[i],所以一定是从前向后遍历。

    for (int i = 0; i < nums.length - 1; i++) {
        if (nums[i + 1] > nums[i]) { // 连续记录
            dp[i + 1] = dp[i] + 1; // 递推公式
        }
    }

    5.举例推导dp数组

    代码:

    class Solution {
        public int findLengthOfLCIS(int[] nums) {
            int[] dp = new int[nums.length];
            for (int i = 0; i < dp.length; i++){
                dp[i] = 1;
            }
            int res = 1;
            for (int i = 0; i < nums.length - 1; i++) {
                if (nums[i + 1] > nums[i]) {
                    dp[i + 1] = dp[i] + 1;
                }
                res = res > dp[i + 1] ? res : dp[i+1]; 
            }
            return res;
        }
    }

    贪心

    class Solution {
        public int findLengthOfLCIS(int[] nums) {
            if (nums.length == 0) return 0;
            int result = 1;
            int count = 1;
            for (int i = 0; i < nums.length - 1; i++){
                if (nums[i + 1] > nums[i]){
                    count++;
                } else {
                    count = 1; 
                }
                if (count > result) result = count;
            }
            return result;
        }
    }

    718.最长重复子数组

    基本思想:

    题目中说的子数组,其实就是连续子序列。

    具体操作:

    1.确定dp数组(dp table)以及下标的含义

    dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。

    定义dp[i][j]为 以下标i为结尾的A,和以下标j 为结尾的B,最长重复子数组长度。处理起来麻烦。

    2.确定推导公式

    根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。

    即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;

    根据递推公式可以看出,遍历i 和 j 要从1开始

    3.dp数组如何初始化

    根据dp[i][j]的定义,dp[i][0] 和dp[0][j]其实都是没有意义的

    dp[i][0] 和dp[0][j]要初始值,因为 为了方便递归公式dp[i][j] = dp[i - 1][j - 1] + 1;

    所以dp[i][0] 和dp[0][j]初始化为0。

    4.确定遍历顺序

    从前向后

    5.举例推导

    代码:

    class Solution {
        public int findLength(int[] nums1, int[] nums2) {
            int result = 0;
            int[][] dp = new int[nums1.length + 1][nums2.length + 1];
            
            for (int i = 1; i < nums1.length + 1; i++) {
                for (int j = 1; j < nums2.length + 1; j++) {
                    if (nums1[i - 1] == nums2[j - 1]) {
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                        result = Math.max(result, dp[i][j]);
                    }
                }
            }
            
            return result;
        }
    }

    1143、最长公共子序列

    基本思想:

    与上一题的区别在于这里不要求是连续的

    具体实现:

    1.确定dp数组(dp table)以及下标的含义

    dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]

    2.确定递推公式

    两种情况:

    • text1[i - 1] 与 text2[j - 1]相同
      • 找到了一个公共元素,dp[i][j] = dp[i - 1][j - 1] + 1;  
    • text1[i - 1] 与 text2[j - 1]不相同
      • 取text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列的最大值

    3.dp数组如何初始化

    test1[0, i-1]和空串的最长公共子序列是0===》dp[i][0] = 0;

    同理dp[0][j]也是0。

    其他下标都是随着递推公式逐步覆盖,初始为多少都可以,那么就统一初始为0。

    4.递推顺序

    有三个方向可以推出dp[i][j],

    在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后,从上到下来遍历这个矩阵。

    5.举例推导dp数组

    代码:

    class Solution {
        public int longestCommonSubsequence(String text1, String text2) {
            int[][] dp = new int[text1.length() + 1][text2.length() + 1];
            for (int i = 1; i <= text1.length(); i++) {
                char char1 = text1.charAt(i - 1);
                for (int j =1; j <= text2.length(); j++) {
                    char char2 = text2.charAt(j - 1);
                    if (char1 == char2) {
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                    } else {
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                    }
                }
            }
            return dp[text1.length()][text2.length()];
        }
    }

    1035、不相交的线

    基本思想:

    绘制一些连接两个数字 A[i] 和 B[j] 的直线,只要 A[i] == B[j],且直线不能相交!

    直线不能相交,这就是说明在字符串A中 找到一个与字符串B相同的子序列,且这个子序列不能改变相对顺序,只要相对顺序不改变,链接相同数字的直线就不会相交。

    本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!

    具体实现:

    与上一题相同

    代码:

    class Solution {
        public int maxUncrossedLines(int[] nums1, int[] nums2) {
            int [][] dp = new int[nums1.length + 1][nums2.length + 1];
            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;
                    } else {
                        dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
                    }
                }
            }
            return dp[nums1.length][nums2.length];
        }
    }
  • 相关阅读:
    数据结构与算法10 微服务接口的鉴权和限流 [MD]
    .Net开发环境配置[OS/IIS/VS...]
    一、单件模式
    正则表达式调试器1.1
    C#2.0新特性系列文章转载
    巧用VS2005解决VS2005网站发布不便问题
    配置VS2005,加速VS2005运行速度
    转载:ASP.NET运行机制 和 图片盗链问题
    ASP.NET页面提前处理问题
    关于NTLM认证的python和.NET实现
  • 原文地址:https://www.cnblogs.com/zhaojiayu/p/15709026.html
Copyright © 2011-2022 走看看