动态规划(四) —— 子序列和子串系列问题总结

news2024/11/27 14:35:04

前言

        通过前面有关动态规划经典问题如背包问题、打家劫舍系列问题和股票投资问题的学习相信小伙伴跟荔枝一样对于动态规划题目有了一定的感觉。接下来再这篇文章中荔枝会继续梳理有关动态规划的经典系列问题——子序列和子串问题,给出解题的分析思路和具体的题解,希望能帮助到有需要的小伙伴。


文章目录

前言

一、Leecode300. 最长递增子序列

1.1 题目分析

1.2 题解示例

二、Leecode674.最长连续递增子序列

2.1 题目分析

2.2 题解示例

三、Leecode718.最长重复子数组

3.1 题目分析

3.2 题解示例

四、Leecode1143.最长公共子序列

4.1 题目分析

4.2 题解示例

五、Leecode1035.不相交的线

5.1 题目分析

5.2 题解示例

六、Leecode53.最大子序和

6.1 题目分析

6.2 题解示例

总结


一、Leecode300. 最长递增子序列

题目描述:

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

输入样例:

        nums = [10,9,2,5,3,7,101,18]

输出样例:

        4

1.1 题目分析

从题目中的描述中我们知晓要求的是最长的严格递增子序列长度,子序列可以不连续但相对位置不能发生改变。按照动态规划的解题顺序来分析一下这道题目:

dp数组的含义

dp[i]:i之前包括i的以nums[i]结尾的最长递增子序列的长度

dp数组推导式

        下标为i的dp[i]代表着i之前包括i的以nums[i]结尾的最长递增子序列的长度,如果 j 在 i 的左侧且nums[i] > nums[j],那么dp[i]就取到 j 从0到  i-1 的所有递增子序列的最大值+1并更新dp[i]。简单来说其实就是对i的左侧进行遍历,如果出现nums[i] > nums[j]则代表着dp[i]的数组进行

for(int j = 0;j<i;j++){
    if(nums[i]>nums[j]) dp[i] = max(dp[i],dp[j]+1);
}

初始化和遍历顺序

        由于我们这里求的是递增子序列,因此我们需要依赖于前面的元素和当前的值作比较,所以这里采用正序遍历比较好。而不管遍历哪一个下标值,最小的递增子序列的长度就是1,所以就把dp数组中的所有元素都初始化为1。

1.2 题解示例

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()<=1) return nums.size();
        vector<int> dp(nums.size(),1);
        int result = 0;
        for(int i=1;i<nums.size();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];
        }
        return result;
    }
};
// dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


二、Leecode674.最长连续递增子序列

题目描述:

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

输入样例:

        nums = [1,3,5,4,7]

输出样例:

        3

2.1 题目分析

        这道题目相比于Leecode300.最长递增子序列而言多了一个要求,那就是要求的这个子序列是一个连续递增的子序列,那么我们的dp数组推导式就要随之发生变化,因为是连续递增的子序列,所以相对来说这道题目的难度应该是会更小一点。

dp数组的含义

        dp[i]:i之前包括i的以nums[i]结尾的最长递增子序列的长度

dp数组推导式

因为是连续递增,所以我们不需要再遍历前面的所有递增子序列和dp[i]来比较大小了,而是仅需要再满足nums[i]>nums[i-1]这个要求之后取到dp[i-1]+1的大小并随之更新dp[i]来获得最大的递增子序列。

//需要注意的是这里的max()是为了获得最大值并更新dp[i] 
if(nums[i]>nums[i-1]) dp[i] = max(dp[i],dp[i-1]+1);

初始化和遍历顺序

         这里的初始化和遍历顺序的选择与求非连续递增子序列是一样的。

2.2 题解示例

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        if(nums.size()==0) return 0;
        vector<int> dp(nums.size(),1);
        int result = 1;
        for(int i=1;i<nums.size();i++){
            if(nums[i]>nums[i-1]) dp[i] = max(dp[i],dp[i-1]+1);
            result = max(result,dp[i]);
        }
        return result;
    }
};
//dp[i]:以i结尾的最长连续递增的子序列的长度

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-continuous-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


三、Leecode718.最长重复子数组

题目描述:

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

输入样例:

        nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]

输出样例:

        3

3.1 题目分析

通过前面两个问题的学习,我们知晓了如何求一个数组中的最长递增序列,而这道题目是求解两个数组中的最长公共子数组,由于是数组,所以一定是连续的。首先我们依旧需要明确dp数组的含义,对于这道题目由于是比较两个数组,所以我们自然而然地就想出使用一个二维数组来表示两个数组中地元素遍历情况。

dp数组的含义

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

dp数组推导式

确定好dp数组的含义后,我们知晓对于dp[i][j]来说,它代表着A数组取到下标为i-1和B数组取到下标为 j-1的时候取到的最长重复子数组的长度,如果nums1[i-1]和nums2[j-1]是相等的,那么就意味着dp[i][j] = A数组取到下标为i-2和B数组取到下标为 j-2的时候取到的最长重复子数组的长度+1

if(nums1[i-1]==nums2[j-1]){
    dp[i][j] = dp[i-1][j-1]+1;
}

初始化和遍历顺序

        由于我们求解的是两个数组之间的最长公共子数组,其最小值为0,因此再初始化的时候可以将所有的dp数组都初始化为0。遍历顺序的选择我们需要根据dp数组的推导式来做出判断,我们知晓当前状态是有前一个状态来推导出来的,所以我们选择正序遍历并选择一个变量result来更新最大的公共子序列。 

3.2 题解示例

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        vector<vector<int>> dp(nums1.size()+1,vector<int>(nums2.size()+1,0));
        int result = 0;
        for(int i=1;i<=nums1.size();i++){
            for(int j=1;j<=nums2.size();j++){
                if(nums1[i-1]==nums2[j-1]) dp[i][j] = dp[i-1][j-1]+1;
                result = max(result,dp[i][j]);
            }
        }
        return result;
    }
};
//只要想到用二维数组可以记录两个字符串的所有比较情况,这样就比较好推递推公式了
//dp[i][j]:以下标i-1为结尾的A,和以下标j-1为结尾的B,最长重复子数组长度为dp[i][j]

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-continuous-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


四、Leecode1143.最长公共子序列

题目描述:

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

输入样例: 

        text1 = "abcde", text2 = "ace" 

输出样例:

        3

4.1 题目分析

注意题目的描述,这道题目其实要我们求的是最长重复子序列! 

dp数组的含义

        dp[i][j]:以下标i-1为结尾的字符串text1和以下标j-1为结尾的字符串B的最长重复子序列长度为dp[i][j]

dp数组推导式

        确定好dp数组的含义后,我们知晓对于dp[i][j]来说,如果text1[i - 1] == text2[j - 1],那么我们只需要知晓i-2和j-2时候的最长重复子序列的长度再+1即可。如果不相等,由于题目要求的是相对位置保持不变,所以这时候需要判断两个数组分别回撤一个下标后取得的最长重复子序列的值即可。

if(text1[i - 1] == text2[j - 1]) {
    dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}

初始化和遍历顺序

对于dp[0][j]和dp[i][0]来说,如果是空串最长的公共子序列就是0,因此我们把他们初始化为0即可。遍历的时候根据dp递推式是从前到后,从上到下的遍历顺序来执行遍历的,因此均设置为正序遍历即可。

4.2 题解示例

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
        for (int i = 1; i <= text1.size(); i++) {
            for (int j = 1; j <= text2.size(); j++) {
                if (text1[i - 1] == text2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[text1.size()][text2.size()];
    }
};

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/longest-common-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

易错点:在遍历的时候一定一定要注意的是——dp数组的含义不能忘,为什么要从1开始遍历,为什么选到nums.size(),这些需要考虑。 


五、Leecode1035.不相交的线

题目描述:

在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:nums1[i] == nums2[j],且绘制的直线不与任何其他连线(非水平线)相交。请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。以这种方法绘制线条,并返回可以绘制的最大连线数。

输入示例: 

        nums1 = [1,4,2], nums2 = [1,2,4]

输出示例:

        2

5.1 题目分析

        这道题目的难点在于如何对题目求解的问题进行转换成求解最长子序列,做完前面的题目其实我们发现,我们在最长子序列求解是的遍历是不回退的,也就是一旦n1[i] == n2[j],只要保证两个数组的相对位置不变,那么相同元素连成的线也就一定不相交。剩下的过程其实跟上一题求解子序列是一样的,大家可以发现甚至代码只需要改个数组名字就行哈哈哈~

5.2 题解示例

class Solution {
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
        vector<vector<int>> dp(nums1.size()+1,vector<int>(nums2.size()+1,0));
        for(int i=1;i<=nums1.size();i++){
            for(int j=1;j<=nums2.size();j++){
                if(nums2[j-1]==nums1[i-1]){
                    dp[i][j] = dp[i-1][j-1]+1;
                }else{
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[nums1.size()][nums2.size()];
    }
};

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/uncrossed-lines
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


六、Leecode53.最大子序和

题目描述:

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。

输入样例:

        nums = [-2,1,-3,4,-1,2,1,-5,4]

输出样例:

        6

6.1 题目分析

明确dp数组含义

        dp[i]:代表着到i的连续数组的最大和

dp数组推导式

这道题目跟背包问题很像,dp[i]的最大值是由两个状态来推导出来的:nums[i]加入当前连续子序列和以及从当前元素开始计算,所以dp数组的推导式可以表示为:

dp[i] = max(dp[i-1]+nums[i],nums[i]);

初始化和遍历顺序

从递推公式可以看出来dp[i]是依赖于dp[i - 1]的状态,dp[0]就是递推公式的基础,而dp[0]的值应该是选择nums[0],同时我们还需要借助一个result来记录连续数组的最大值。

6.2 题解示例

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if (nums.size() == 0) return 0;
        vector<int> dp(nums.size(),0);
        dp[0] = nums[0];
        int result = nums[0];
        for(int i=1;i<nums.size();i++){
            dp[i] = max(dp[i-1]+nums[i],nums[i]);
            if(result<dp[i]) result = dp[i];
        }
        return result;
    }
};
// 贪心解法
// class Solution {
// public:
//     int maxSubArray(vector<int>& nums) {
//         int result = INT32_MIN;
//         int count = 0;
//         for(int i=0;i<nums.size();i++){
//             count+=nums[i];
//             if(count>result){
//                 result = count;
//             }
//             if(count<=0){
//                 count = 0;
//             }
//         }
//         return result;
//     }
// };

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


总结

        最后,荔枝总结一下子序列和子串问题的难点吧。第一个是有关dp数组的设置,相信有些小伙伴会疑问为什么要设置成dp[i][j]代表着两个数组取i-1,j-1的时候的最大公共子序列,这其实是为了节省单独初始化dp[0][j]和dp[i][0]的步骤,但是带来的问题就是再写代码的时候会很容易就弄乱了思路,这时候就需要谨记dp数组的含义咯~接着其实就是转化的问题,对于不相交的直线我们怎么理解成求最大公共子序列问题;最后一个就是记得区分子串和子序列,两者在连续性上是不同滴,大家可以体会一下下哈哈哈,一起加油吧~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/769775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

曲阜师范大学2023年暑期大一新生排位赛 题解

目录 A (1). Sum 详细点击&#xff1a;sum //整除分块 B (2). Sort C (3). String //字符串dp D (4). Factor ​​​​​​​ ​​​​​​​ //素数筛变式 E (5). Tree ​​​​​​​ …

复现Nature图表:GSEA分析及可视化包装函数

这篇帖子主要的目的是写一个转录组GSEA分析和可视化通用的函数。起因是我们想要复现一篇文章的GSEA可视化图片&#xff0c;这个Nature文章GSEA可视化挺好的&#xff1a; image.png &#xff08;reference&#xff1a;B-cell-specific checkpoint molecules that regulate anti…

vscode报警和报错没有颜色

前言 解决方法来源 https://www.zhihu.com/question/506531863 解决步骤 安装IPython conda install IPython打开/anaconda3/envs/mmagic3/lib/python3.8/site-packages&#xff0c;然后创建一个文件&#xff0c;sitecustomize.py&#xff0c;里面写入 import sys frome IP…

Python(二十一)intput()函数的高级使用

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

Kafka消息队列基础入门和实战例子

1、Kafka 1.1 Kafka部署配置 1.1.1 下载Kafka 下载Kafka https://kafka.apache.org/downloads.html https://archive.apache.org/dist/kafka/2.4.1/kafka_2.11-2.4.1.tgz下载Scala-2.11版本 Scala-2.11经典版本解压 直接解压到某个目录&#xff0c;可以一起放在Java相关的…

MathType公式编辑器右边选项变灰

今天在写论文的时候&#xff0c;想要给公式添加编号&#xff0c;发现Word中的MathType好多选项都变灰了&#xff0c;然后查找了一些资料&#xff0c;最终解决&#xff0c;这里记录一下&#xff0c;方便以后查阅。 MathType公式编辑器右边选项变灰 问题描述解决方案禁止MathType…

C++---树形DP---树的中心(每日一道算法2023.7.19)

注意事项&#xff1a; 本题为"树形DP—树的最长路径"的近似题&#xff0c;同时涉及到 单链表模拟邻接表存储图 的操作&#xff0c;建议先理解那篇文章。 题目&#xff1a; 给定一棵树&#xff0c;树中包含 n 个结点&#xff08;编号1~n&#xff09;和 n−1 条无向边…

pandas清洗客户编码异常数据

前言 在不同行业中&#xff0c;我们经常会遇到一个麻烦的问题&#xff1a;数据清洗。尤其是当我们需要处理客户编码异常数据时&#xff0c;这个问题变得尤为重要。想象一下&#xff0c;许多银行都是以客户为单位管理数据的&#xff0c;因此每个客户都有一个独特的编码。在处理…

LiveGBS流媒体平台GB/T28181功能-海康NVR摄像机自带物联网卡摄像头注册GB/T28181国标平台看不到设备的时候如何抓包及排查

海康大华宇视华为等硬件NVR摄像机注册到LiveGBS国标平台看不到设备的时候如何抓包及排查 1、设备注册后查看不到1.1、是否是自带物联网卡的摄像头1.2、关闭萤石云1.3、防火墙排查1.4、端口排查1.5、IP地址排查1.6、设备TCP/IP配置排查1.7、设备多网卡排查1.8、设备接入配置参数…

实战:ELK环境部署并采集springboot项目日志

文章目录 前言技术积累ELK组成及功能框架搭建基础 EIK环境搭建elasticsearch配置相关kibana配置相关logstash配置相关elk目录下增加docker-compose文件查看elk目录文件树编排elk springboot集成logstashpom.xmllogback-spring.xml启动项目logstash采集日志 写在最后 前言 相信…

Java8 stream toMap、groupingBy、mapping的综合应用

文章目录 一、stream toMap、groupingBy、mapping的综合应用1、前提准备①、实体类②、数据准备 2、核心代码&#xff1a;3、运行结果 一、stream toMap、groupingBy、mapping的综合应用 1、前提准备 ①、实体类 package com.cfay.demo;import lombok.AllArgsConstructor; i…

LCD拼接屏、LED显示屏和OLED显示屏的主要区别

我们在生活或工作中经常看到大大小小的显示屏&#xff0c;但很多人却分不清楚这些屏到底属于哪一类&#xff0c;今天sostron与大家一起来分享下关于&#xff1a;LCD拼接屏、LED显示屏、OLED透明屏三者的区别。 LCD拼接屏、LED显示屏和OLED显示屏是不同类型的显示技术&#xff0…

【116个】网络安全测试相关面试真题

1、Burpsuite常用的功能是什么&#xff1f; 2、reverse_tcp和bind_tcp的区别&#xff1f; 3、拿到一个待检测的站或给你一个网站&#xff0c;你觉得应该先做什么&#xff1f; 4、你在渗透测试过程中是如何敏感信息收集的&#xff1f; 5、你平时去哪些网站进行学习、挖漏洞提交到…

这样创建客户帮助中心,效果超好!

创建一个有效的客户帮助中心是为了为客户提供优质的支持和服务。在这个数字化时代&#xff0c;客户期望能够快速找到所需的信息&#xff0c;并得到准确和及时的解答。本文将分享创建有效客户帮助中心的最佳实践&#xff0c;帮助您提供出色的客户体验并提升客户满意度。 1. 了解…

Banana Pi M2 Zero 运行 openHAB 回顾

首先我要透露的是&#xff0c;BPI 的工作人员向我发送了一台免费的 BPi M2 Zero 来执行这些测试。我相信我的评论是公平和公正的&#xff0c;但我想坦率地说明这一事实。 硬件简介 与 Raspberry Pi Zero W 相比&#xff0c;Banana Pi BPI-M2 Zero 具有令人印象深刻的规格。以下…

git进阶操作

一、git 基础概念1. 1.1 三种状态&#xff1a; 工作区&#xff08;unstage&#xff09;——已修改&#xff08;modified&#xff09; 暂存区&#xff08;stage&#xff09;——已暂存&#xff08;staged&#xff09; 对象区——已提交&#xff08;commited&#xff09; 工作…

moment.js常见格式化处理各种时间方法

Moment.js 是一个简单易用的轻量级 JavaScript 日期处理类库,提供了日期格式化、日期解析等功能。它支持在浏览器和 NodeJS 两种环境中运行。此类库能够将给定的任意日期转换成多种不同的格式,具有强大的日期计算功能,同时也内置了能显示多样的日期形式的函数。另外,它也支…

博弈论--sg函数

sg函数------ 定义终止状态的SG函数值为0。如果游戏已经结束&#xff0c;即达到了终止状态&#xff0c;那么对应的SG函数值就是0。即先手的sg值为0&#xff0c;则先手必败&#xff0c;否则先手必胜。 如何求sg函数值--------对于每个可能的移动&#xff0c;将后续状态的SG函数…

「从零入门推荐系统」21:chatGPT、大模型介绍

作者 | gongyouliu 编辑 | gongyouliu 自2022年11月30日OpenAI发布chatGPT以来&#xff0c;大模型技术掀起了新一轮人工智能浪潮。chatGPT在各个领域&#xff08;包括对话、摘要、内容生成、问题解答、识图、数学计算与推理、代码编写等&#xff09;取得了比之前算法好得多的成…

测试开发之路 (工具篇)--Docker

目录 前言 什么是 docker 在 demo 中学习 mysql test link 更复杂点的场景 前言 Docker是一种开源的容器化平台&#xff0c;它可以帮助开发人员和测试人员更轻松地构建、部署和运行应用程序。在测试开发中&#xff0c;Docker可以提供许多便利和优势。 什么是 docker 官…