动态规划---最长连续子序列问题

这篇具有很好参考价值的文章主要介绍了动态规划---最长连续子序列问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说明

最长连续子序列问题算是动态规划问题中的一个小分支,这里单独写一篇文章介绍。至于动态规划基础问题和详细的处理步骤我在我的另一篇文章中详细介绍过。具体解决步骤请移步观看——动态规划基础篇。如果想了解01背包问题和滚动数组相关内容请移步观看——动态规划——01背包问题。

例题讲解

1.最长连续递增序列

给定一个未经排序的整数数组,找到最长且连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], …, nums[r - 1], nums[r]] 就是连续递增子序列。
提示:

  • 1 <= nums.length <= 104
  • 109 <= nums[i] <= 109
1.1确定dp(dp table)数组及其下标的含义

dp[i]:以nums[i]结尾的最长的递增且连续的子序列长度为dp[i]。
注意这里的以nums[i]的子序列必须是连续递增序列,例如nums=[1,5,7,4],dp[3]=1,即以nums[3]=4为结尾的连续递增子序列为[4]。

1.2确定递推公式

我们如何得到dp[i],注意dp数组的含义:以nums[i]结尾的最长的递增且连续的子序列长度为dp[i]。因为要求是连续的递增子数列,所以dp[i]只能由dp[i-1]递推得出,如果nums[i]>nums[i-1]显然,dp[i]=dp[i-1]+1,如果nums[i]<=nums[i-1],nums[i]无法接在nums[i-1]后面此种情况直接跳过。

综上分析,递推公式如下:

if(nums[i]>nums[i-1]){
	dp[i]=dp[i-1]+1;
}
1.3dp数组初始化

因为以下标i为结尾的数组的连续递增的⼦序列⻓度最少也应该是1,即就是nums[i]这⼀个元素,所以dp[i]初始值应该为1。

1.4代码示例
class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int len=nums.length;
        int[] dp=new int[len];
        Arrays.fill(dp,1);
        int max=1;
        for(int i=1;i<len;i++){
            if(nums[i]>nums[i-1]){
                dp[i]=dp[i-1]+1;
            }
            max=Math.max(max,dp[i]);
        }
        return max;
    }
}

注意:这里我来讲一下这段代码" max=Math.max(max,dp[i])"的意义,首先我们将dp数组初始值都设为以,如果nums[i]>nums[i-1],则dp[i]=dp[i-1]+1,但是如果nums[i]<=nums[i-1],我们没有对dp数组做任何处理,这样我们虽然是从i=1开始递推,但是我们不能保证dp[len-1]是最大值,也就是我们需要的结果,因为如果nums[len-1]<=nums[len-2]那么dp[len-1]不会做任何处理,等于初始值1,所以我们不能保证dp数组最后一个值是递推的最终结果,所以我们需要额外借用一个变量max保存最大值。

2.最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
提示:

  • 1 <= nums.length <= 2500
  • 104 <= nums[i] <= 104
2.1确定dp(dp table)数组及其下标的含义

dp[i]:以nums[i]结尾的最长的递增子序列长度为dp[i]。
注意这里的以nums[i]的子序列必须是递增序列,例如nums=[1,5,7,4],dp[3]=2,即以nums[3]=4为结尾的递增子序列为[1,4]。

2.2确定递推公式

这道题和第一道例题最大的区别就是,不再要求递增数列是连续的。所以我们要的道dp[i],就需要让nums[i]和前j个元素(j<i)比较,如果nums[i]>nums[j],那么dp[i]=Math.max(dp[i],dp[j]+1)。至于为什么dp[i]=Math.max(dp[i],dp[j]+1),而不是dp[i]=dp[j]+1,这道题讲解最后我会给大家举一个例子帮助大家理解。

综上分析,递推公式如下:

if(nums[i]>nums[j]){
	dp[i]=Math.max(dp[i],dp[j]+1);
}
2.3dp数组初始化

因为以下标i为结尾的数组的递增的⼦序列⻓度最少也应该是1,即就是nums[i]这⼀个元素,所以dp[i]初始值应该为1。

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

虽然现在这道题代码已经写出来了,但是大家可能还会疑问为什么dp[i]=Math.max(dp[i],dp[j]+1),而不是dp[i]=dp[j]+1,因为前面这里我并没有详细展开叙述,而是一笔带过,这里我会该大家举一个具体的例子,帮助大家理解。
假设nums=[1,5,7,4,3,8]
当我们从i=1,j=0开始遍历,显然nums[1]>nums[0],所以dp[1]=Math.max(dp[1],dp[0]+1)=2;
当我们从i=2,j=0开始遍历,显然nums[2]>nuns[0],nums[2]>nums[1],所以最终dp[2]=3;
当我们从i=3,j=0开始遍历,显然nums[3]=4,他只大与nums[0]=1,所以最终dp[3]=2;
当我们从i=4,j=0开始遍历,显然nums[4]=3,他只大与nums[0]=1,所以最终dp[4]=2;
当我们从i=5,j=0开始遍历,此时nums[5]=8,是这个数组中最大数字,当遍历道j=2时,nums[5]=8>nums[2]=7,所以dp[5]=Math.max(dp[5],dp[2]+1)=4,注意这里直接让dp[5]=dp[2]+1也等于4,因为dp[5]初始值为1,但是当遍历到j=3时,nums[5]=8>nums[3]=4,如果直接让dp[5]=dp[3]+1,按照之前的推理,dp[3]=2,那么dp[5]=dp[3]+1=3,显然这是不对的。
所以我们要保存最大的结果就到让dp[5]=Math.max(dp[5],dp[3]+1);

通过这个例子,其实我们也可以发现假如nums=[1,5,7,4],那么dp[3]=2,同样dp数组的最后一个值并不是地推过程中产生的最大值,所以我们同样需要一个额外的变量max保留递推过程中产生的最大值。

3.最长重复子数组

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
提示:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 100
3.1确定dp(dp table)数组及其下标的含义

dp[i][j]:代表以 nums1[i-1] 结尾和 nums2[j-1] 结尾构成的最长公共子数组的长度。
例如nums1=[0,1,1,1,1],nums2=[1,0,1,0,1];
公共子数组为nums1中的[0,1]和nums2中的[0,1],但是要注意nums2中的[0,1]由两个一个时以第2个1结尾的[0,1]数组另一个是以第3个1结尾的[0,1]数组。
我们只需要找到最大的公共子数组即可。

3.2确定递推公式

dp[i][j]=dp[i-1][j-1]+1;

3.3dp数组初始化

根据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。
举个例⼦A[0]如果和B[0]相同的话,dp[1][1] = dp[0][0] + 1,只有dp[0][0]初始为0,正好符合递推公式
逐步累加起来。

3.4代码示例
class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int len1=nums1.length,len2=nums2.length;
        int[][] dp=new int[len1+1][len2+1];
        int res=0;
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
               if(nums1[i-1]==nums2[j-1]){
                   dp[i][j]=dp[i-1][j-1]+1;
               }
               res=Math.max(res,dp[i][j]);
            }
        }
        return res;
    }
}

4.最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1 和 text2 仅由小写英文字符组成。
4.1确定dp(dp table)数组及其下标的含义

dp[i][j]:表示第一个字符串前i-1个字符和第二个字符串前j-1个字符组成的最长公共字符串的长度。

4.2确定递推公式

如果text1.charAt(i-1)==text.charAt(j-1),显然dp[i][j]=dp[i-1][j-1],如text1=“abde”,text2=“acd”,text1.charAt(2)==text2.charAt(2),显然dp[3][3]=dp[2][2]+1,即"abd"和"acd"组成的最长公共字符串的长度等于"ab"和"ac"组成的公共子串的长度+1;
如果text1.charAt(i-1)==text.charAt(j-1),注意dp数组的含义:表示第一个字符串前i-1个字符和第二个字符串前j-1个字符组成的最长公共字符串的长度。还是上面的例子,dp[2][1]=1,即"ab"和"a"组成的最长公共字符串的长度为1,dp[1][2]=1,即即"a"和"ac"组成的最长公共字符串的长度为1,假设此时i=2,j=2,text1.charAt(i-1)=text1.charAt(1),text2.charAt(j-1)=text1.charAt(1),此时text1.charAt(1)!=text2.charAt(1)即’b’!=‘c’,注意dp[2][2]表示第一个字符串前1个字符和第二个字符串前1个字符组成的最长公共字符串的长度(字符串字符下标从0开始),此时dp[2][2]有两种选择要么dp[2][2]=dp[2][1],要么dp[2][2]=dp[1][2],二者去最大值即可,即dp[2][2]=Math.max(dp[2][1],dp[1][2]);

综上,递推公式为:

if(text1.charAt(i-1)==text2.charAt(j-1)){
	dp[i][j]=dp[i-1][j-1]+1;
}else{
    dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
}
4.3dp数组初始化

根据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。
举个例⼦text1.charAt(0)如果和text2.charAt(0)相同的话,dp[1][1] = dp[0][0] + 1,只有dp[0][0]初始为0,正好符合递推公式
逐步累加起来。文章来源地址https://www.toymoban.com/news/detail-605593.html

4.4代码示例
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
            int len1=text1.length(),len2=text2.length();
            int[][] dp=new int[len1+1][len2+1];
            for(int i=1;i<=len1;i++){
                for(int j=1;j<=len2;j++){
                    if(text1.charAt(i-1)==text2.charAt(j-1)){
                        dp[i][j]=dp[i-1][j-1]+1;
                    }else{
                        dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
                    }
                }
            }
            return dp[len1][len2];
    }
}

到了这里,关于动态规划---最长连续子序列问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态规划应用篇:详解最长公共子序列问题

    动态规划 是一个强大的工具,将复杂问题 分解 为多个容易解决的子问题,并且会对中间结果进行存储从而避免重复计算,然后将它们的解组合起来,形成大问题的解,高效地得出 全局最优解 。前面我们已经了解了动态规划的基础知识及一维动态规划问题的求解,今天,我

    2024年04月15日
    浏览(46)
  • 【算法(四·三):动态规划思想——最长公共子序列问题】

    最长公共子序列(Longest Common Subsequence,简称LCS)问题是一种常见的字符串处理问题。它的**目标是找到两个或多个字符串中的最长公共子序列,这个子序列不需要是连续的,但字符在原始字符串中的相对顺序必须保持一致。**例如,考虑两个字符串\\\"ABCD\\\"和\\\"ACDF\\\",它们的最长公

    2024年04月13日
    浏览(50)
  • 动态规划9:最长递增子序列、最长连续递增序列、最长重复子数组、最长公共子序列、不相交的线、最长子序和

    例题300: 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 确定dp数组和下标含义 dp[i]表示在第i个元素的最长子序列数

    2024年04月08日
    浏览(44)
  • 算法打卡day49|动态规划篇17| Leetcode 647. 回文子串、516.最长回文子序列

    Leetcode 647. 回文子串 题目链接:647. 回文子串 大佬视频讲解:647. 回文子串视频讲解  个人思路  这道题的dp数组有点难找到关联,以至于递归关系也不好找,所以看题解吧... 解法 动态规划 动规五部曲: 1.确定dp数组(dp table)以及下标的含义 一般在定义dp数组的时候 会根据题

    2024年04月22日
    浏览(49)
  • leetcode300. 最长递增子序列(动态规划-java)

    来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/longest-increasing-subsequence 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序

    2024年02月15日
    浏览(44)
  • 【LeetCode: 673. 最长递增子序列的个数 | 动态规划】

    🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,CSDN-Java领域新星创作者🏆,保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文

    2024年02月03日
    浏览(64)
  • leetcode1143. 最长公共子序列-动态规划(java)

    leetcode1143. 最长公共子序列 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/longest-common-subsequence 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。 一个字符串的 子序列 是指这样一个新的字符串: 它是由原字

    2024年01月19日
    浏览(43)
  • 每天一道leetcode:516. 最长回文子序列(动态规划&中等)

    给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。 1 = s.length = 1000 s 仅由小写英文字母组成 动态规划 ,使用二维dp数组记录[i,j]间的最大回文子序列长度

    2024年02月13日
    浏览(47)
  • ( 动态规划) 516. 最长回文子序列 ——【Leetcode每日一题】

    难度:中等 给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。 示例 1: 输入:s = “bbbab” 输出:4 解释:一个可能的最长回文子序列为 “bbbb” 。 示例

    2024年02月06日
    浏览(44)
  • 算法:动态规划——最长公共子序列

    动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。 与分治法不同的是,适合于用动态规划法求解的问题,经分解得到的子问题往往不是互相独立的。若用分治法解这类问题,则分解得到的

    2023年04月27日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包