代码随想录Day 43|leetcode题目:300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

news2024/11/19 7:49:09

提示:DDU,供自己复习使用。欢迎大家前来讨论~

文章目录

  • 动态规划Part10
    • 题目一:300.最长递增子序列
      • 解题思路:
    • 题目二:674. 最长连续递增序列
      • 解题思路:
    • 题目三: 718. 最长重复子数组
      • 解题思路
      • 滚动数组
  • 总结

动态规划Part10

题目一:300.最长递增子序列

300. 最长递增子序列

解题思路:

  • 外层循环:遍历数组 nums 的每个元素,用索引 i 表示当前考虑的元素。

  • 内层循环:对于每个索引 i,遍历从 0i-1 的所有元素,用索引 j 表示这些元素。

  • 子序列比较:对于内层循环中的每个索引 j,比较 nums[j](子序列的最后一个元素)和 nums[i](当前考虑加入的元素)。

  • 子序列更新

    • 如果 nums[i] 大于 nums[j],则 nums[i] 可以被添加到以 nums[j] 结尾的子序列中,形成一个新的递增子序列。
    • 这意味着,对于每个 nums[i],我们都尝试将其添加到所有可能的递增子序列中。
  • 最长子序列的确定

    • 对于每个 nums[i],我们维护一个变量来记录以 nums[i] 为结尾的最长递增子序列的长度。
    • 这个长度是所有尝试将 nums[i] 添加到以 nums[0]nums[i-1] 为结尾的递增子序列后,得到的最长子序列长度的最大值。
  • 结果:算法结束时,得到的最大值即为整个数组 nums 的最长递增子序列的长度。

正解:从任意位置开始,但以nums【i】元素作为结尾的所有 递增子序列中,最长的子序列长度为 dp【i】

  • dp[i] 表示的是以 nums[i] 为结尾的最长递增子序列的长度,这个子序列可以开始于数组中的任何位置。
  • 动态规划数组 dp 的每个元素 dp[i] 都是独立的,它表示以 nums[i] 为结尾的最长递增子序列,而不是从 nums[0]nums[i] 的连续子序列。
  • 为了计算 dp[i],我们需要检查所有先前的元素 nums[0]nums[i-1],看它们是否可以作为递增子序列的一部分,使得 nums[i] 成为这个子序列的最后一个元素。

用动规五部曲来详细分析一波:

  1. dp[i]的定义

本题中,正确定义dp数组的含义十分重要。

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

  1. 状态转移方程

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

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

注意这里不是要dp[i] 与 dp[j] + 1进行比较,而是我们要取dp[j] + 1的最大值

  1. dp[i]的初始化

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

  1. 确定遍历顺序

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

j其实就是遍历0到i-1,那么是从前到后,还是从后到前遍历都无所谓,只要吧 0 到 i-1 的元素都遍历了就行了。 所以默认习惯 从前向后遍历。

遍历i的循环在外层,遍历j则在内层,代码如下:

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]; // 取长的子序列
}
  1. 举例推导dp数组

输入:[0,1,0,3,2],dp数组的变化如下:

300.最长上升子序列

以上五部分析完毕,C++代码如下:

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;
    }
};
  • 时间复杂度: O(n^2)
  • 空间复杂度: O(n)

题目二:674. 最长连续递增序列

674. 最长连续递增序列

解题思路:

本题要求的是最长连续递增序列

动规五部曲分析如下:

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

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

注意这里的定义,一定是以下标i为结尾,并不是说一定以下标0为起始位置。

  1. 确定递推公式

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

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

    在寻找数组中最长连续递增子序列时,如果 nums[i] 大于 nums[i - 1],则以 nums[i] 为结尾的连续递增子序列长度等于以 nums[i - 1] 为结尾的连续递增子序列长度加1,这可以通过单层循环实现,而不需要两层循环来比较所有元素。

  2. dp数组如何初始化

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

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

  3. 确定遍历顺序

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

    本文在确定递推公式的时候也说明了为什么本题只需要一层for循环,代码如下:

    for (int i = 1; i < nums.size(); i++) {
        if (nums[i] > nums[i - 1]) { // 连续记录
            dp[i] = dp[i - 1] + 1;
        }
    }
    
  4. 举例推导dp数组

    已输入nums = [1,3,5,4,7]为例,dp数组状态如下:

    674.最长连续递增序列

    以上分析完毕,C++代码如下:

    class Solution {
    public:
        int findLengthOfLCIS(vector<int>& nums) {
            if (nums.size() == 0) return 0;
            int result = 1;
            vector<int> dp(nums.size() ,1);
            for (int i = 1; i < nums.size(); i++) {
                if (nums[i] > nums[i - 1]) { // 连续记录
                    dp[i] = dp[i - 1] + 1;
                }
                if (dp[i] > result) result = dp[i];
            }
            return result;
        }
    };
    
    • 时间复杂度:O(n)
    • 空间复杂度:O(n)

题目三: 718. 最长重复子数组

718. 最长重复子数组

解题思路

要找到两个数组中最长的重复子数组(连续子序列),步骤:

  1. 两层循环:首先使用两层循环遍历两个数组,确定可能的起始位置对。
  2. 单层循环或while循环:对于每一对起始位置,从这两个位置开始,逐个比较两个数组中的元素,直到遇到不匹配的元素或达到数组末尾。
  3. 记录长度:在比较过程中,如果元素匹配,则更新一个计数器来记录当前匹配的子数组长度,这将是最长重复子数组的长度。

这种方法的时间复杂度较高,但对于小规模数据或简单场景下是可行的。

动态规划是解决两个字符串之间最长重复子数组问题的有效方法。

  1. 确定dp数组及其下标含义

    • dp[i][j] 表示以数组A的下标 i-1 结尾和数组B的下标 j-1 结尾的最长重复子数组的长度。
    • 注意,这里的下标是从1开始的,因为 dp[0][0] 没有实际含义。
  2. 确定递推公式

    • 如果 A[i-1] 等于 B[j-1],则 dp[i][j] = dp[i-1][j-1] + 1
    • 否则,dp[i][j] = 0,因为不匹配时子数组长度重置为0。
  3. dp数组如何初始化

    • dp[i][0]dp[0][j] 都初始化为0,因为它们没有实际含义,但需要这样的初始化来满足递推公式。
  4. 确定遍历顺序

    • 外层循环遍历数组A,内层循环遍历数组B。

    • 也可以反过来,这不影响算法的正确性,但会影响代码的实现细节。

    • 在遍历过程中,需要记录 dp 数组中的最大值,这个最大值就是两个数组之间最长重复子数组的长度。

代码如下:

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;
        }
        if (dp[i][j] > result) result = dp[i][j];
    }
}
  1. 举例推导dp数组

拿示例1中,A: [1,2,3,2,1],B: [3,2,1,4,7]为例,画一个dp数组的状态变化,如下:

718.最长重复子数组

以上五部曲分析完毕,C++代码如下:

// 版本一
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;
                }
                if (dp[i][j] > result) result = dp[i][j];
            }
        }
        return result;
    }
};
  • 时间复杂度:O(n × m),n 为A长度,m为B长度
  • 空间复杂度:O(n × m)

滚动数组

在如下图中:

718.最长重复子数组

为了优化空间复杂度,可以将二维的dp数组压缩成一维,此时在遍历B数组时需要从后向前进行,以避免在更新dp值时覆盖还未使用的元素。

// 版本二
class Solution {
public:
    int findLength(vector<int>& A, vector<int>& B) {
        vector<int> dp(vector<int>(B.size() + 1, 0));
        int result = 0;
        for (int i = 1; i <= A.size(); i++) {
            for (int j = B.size(); j > 0; j--) {
                if (A[i - 1] == B[j - 1]) {
                    dp[j] = dp[j - 1] + 1;
                } else dp[j] = 0; // 注意这里不相等的时候要有赋0的操作
                if (dp[j] > result) result = dp[j];
            }
        }
        return result;
    }
};
  • 时间复杂度: O ( n × m ) O(n × m) O(n×m),n 为A长度,m为B长度
  • 空间复杂度: O ( m ) O(m) O(m)

总结

  1. 状态定义
    • 300. 最长递增子序列:定义 dp[i] 为考虑到第 i 个元素时,能形成的最长递增子序列的长度。
    • 674. 最长连续递增序列:定义 dp[i] 为以第 i 个元素结尾的最长连续递增序列的长度。
    • 718. 最长重复子数组:定义 dp[i][j] 为数组A的后 i 个元素和数组B的后 j 个元素的最长重复子数组长度。
  2. 状态转移方程
    • 300dp[i] = max(dp[i], dp[j] + 1) 对所有 j < i,如果 nums[j] < nums[i]
    • 674dp[i] = dp[i-1] + 1 如果 nums[i] > nums[i-1],否则 dp[i] = 1
    • 718dp[i][j] = dp[i-1][j-1] + 1 如果 A[i] == B[j],否则 dp[i][j] = 0
  3. 初始化和遍历顺序
    • 初始化:所有 dp 数组的初始值需要根据问题的具体要求来设定,通常是0或1。
    • 遍历顺序:根据状态转移方程的要求,确定如何遍历数组或字符串。对于 300674,通常从前向后遍历;对于 718,可能需要两层嵌套循环来遍历两个数组。

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

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

相关文章

Radiance Field Learners As UAV First-Person Viewers 精读

1. 多尺度相机空间估计模块&#xff1a; 关键帧选择器&#xff1a;自动选择最具代表性的帧进行渲染&#xff0c;减少计算量&#xff0c;提高渲染效率。无人机轨迹预测&#xff1a;通过历史轨迹预测无人机的未来位置&#xff0c;确保实时视角调整&#xff0c;提高无人机导航的准…

云渲染与AI渲染分别是什么?两者的优势对比

云渲染和AI渲染是两种先进的渲染技术&#xff0c;它们各自具有独特的优势和应用场景。下面针对两种情况来简单说明下。 1、云渲染&#xff1a; - 定义&#xff1a;云渲染是一种利用远程服务器(云端)来处理和生成渲染效果的技术。它允许用户将计算密集型的任务转移到云端&#…

[论文笔记] CSFCN

摘要 上下文建模或多级特征融合方法已被证明可以有效提高语义分割性能。 然而&#xff0c;它们并不是专门处理像素上下文不匹配和空间特征不对齐的问题&#xff0c;并且高计算复杂度阻碍了它们在实时场景中的广泛应用。 在这项工作中&#xff0c;我们提出了一种轻量级的上下文…

8.第二阶段x86游戏实战2-实现瞬移

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

【C++11 —— 包装器】

C11 —— 包装器 包装器function包装器function包装器介绍function包装器统一类型function包装器的意义 bind包装器bind包装器介绍bind包装器绑定固定参数bind包装器调整传参顺序bind包装器的意义 包装器 function包装器 function包装器介绍 function包装器 也叫作适配器。C…

【Go】使用Goland创建第一个Go项目

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

go-zero的快速实战(完整)

微服务框架 go-zero 的基本介绍 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性&#xff0c;经受了充分的实战检验。 go-zero 中的 api&#xff0c;rpc&#xff0c;数据库等涉及的代码&#xff0c;都可以给我们一键生成&#…

计算机毕业设计 自习室座位预约系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

RTC、ADC

RTC RTC&#xff08;Real-Time Clock&#xff09;是实时时钟模块&#xff0c;用于跟踪实际时间&#xff08;年、月、日、时、分、秒&#xff09;&#xff0c;即使在系统断电或处于低功耗模式下也能保持时间的准确性。 特点 时间和日期跟踪低功耗模式支持可编程闹钟和定时器备…

贪心算法day31|56. 合并区间、738. 单调递增的数字(整数与字符串的转换)、贪心刷题总结

贪心算法day31|56. 合并区间、738. 单调递增的数字、贪心刷题总结 56. 合并区间738. 单调递增的数字贪心刷题总结 56. 合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 …

大模型微调 - 自动加载预训练模型

大模型微调 - 自动加载预训练模型 flyfish AutoModelFor 是 Hugging Face transformers 库中的一个通用接口&#xff0c;这里用的是modelscope &#xff0c;用于自动加载预训练模型&#xff0c;涵盖多种任务的模型。AutoModelFor 后面接不同的任务名称会对应不同的模型架构&a…

基于 CycleGAN 对抗网络的自定义数据集训练

目录 生成对抗网络&#xff08;GAN&#xff09; CycleGAN模型训练 训练数据生成 下载开源项目CycleGAN 配置训练环境 开始训练 模型测试 可视化结果 生成对抗网络&#xff08;GAN&#xff09; 首先介绍一下什么是GAN网络&#xff0c;它是由生成器&#xff08;Generator…

工具、环境等其他小问题归纳

此篇文章内容会不定期更新&#xff0c;仅作为学习过程中的笔记记录 一、查询Windows 10环境下python版本与安装路径 若电脑成功安装了python环境&#xff0c;不小心忘了版本。 I、查询版本 1、cmd窗口快捷查询 Win R 输入cmd 进入窗口&#xff1b; 直接输入 python --version …

2024.9.13 系统运维

学习目标&#xff1a;了解 云计算运维 “云计算是中国的骄傲&#xff01;” 企业向云服务商租用云服务&#xff08;省钱、省心、省力&#xff09; 云计算&#xff1a;公有云、私有云&#xff08;大公司&#xff0c;数据隐私性&#xff09;、混合云&#xff08;私有云跑重要…

前端刷新进不了登录页面

报错props.ts:15 Uncaught (in promise) SyntaxError: Unexpected token 错误截图&#xff1a; 原因&#xff1a;谷歌浏览器版本过低&#xff0c;升级浏览器 比如这边版本就过低了

ThinkCMF框架任意内容包含漏洞的讲解

本文来自无问社区&#xff0c;更多网安资料可前往查看http://www.wwlib.cn 背景描述 ThinkCMF是一款基于PHPMYSQL开发的中文内容管理框架&#xff0c;底层采用ThinkPHP3.2.3构建。 ThinkCMF提出灵活的应用机制&#xff0c;框架自身提供基础的管理功能&#xff0c;而开发者可…

CSP 2023 提高级第一轮单项选择题解析

CSP 2023 提高级第一轮单项选择题解析 第1题第2题第3题第4题第5题第6题第7题第8题第9题第10题第11题第12题第13题第14题第15题 第1题 在 Linux 系统终端中&#xff0c;以下哪个命令用于创建一个新的目录&#xff1f;(B) A.newdir B.mkdir C.create D.mkfold 解析&#xff1a;记…

部署Tomcat和抓包

部署Tomcat 复制文件到桌面 查看自己是否有java环境&#xff0c;下图所示是有的&#xff0c;若没有需另行下载 解压tomcat文件 tar -xzvf apache-tomcat-7.0.96.tar.gz 下列为tomcat文件的几个重要文件 进入到bin文件中 启动tomcat ./startup.sh 可以先用本机查看是否启动…

【PostgreSQL里的restartpoint重启点】

不知道大家有没有关注过&#xff0c;配置文件里archive_cleanup_command参数的注释部分有着这么一句"command to execute at every restartpoint",意思是在每个restartpoint时执行的命令。 提起checkpoint大家可能比较熟悉&#xff0c;对于这个restartpoint&#xff…

英文软件汉化中文软件教程asi exe dll 等汉化教程

相信大家在使用国际软件的时候&#xff0c;会经常碰到英文类型的软件 或者玩一些游戏使用一些工具&#xff0c;也基本都是外网的&#xff0c;那么对于用户来讲 就会非常的不方便&#xff01; 小编为大家整理了一些国内大佬出的的英文软件汉化中文软件的视频教程 教程分为EX…