Day52【动态规划】300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

news/2024/11/20 0:43:34/

300.最长递增子序列

力扣题目链接/文章讲解 

视频讲解 

1、确定 dp 数组下标及值含义

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

dp[i]:下标 i 表示以 nums[i] 结尾的最长递增子序列,dp[i] 的值表示该子序列长度的

2、确定递推公式

要求 dp[i],需要考虑以 nums[i] 结尾的最长递增子序列,注意该子序列一定包含 nums[i] 了

我们可以按照下述方法构建所有以 nums[i] 结尾的递增子序列,再在里面找最长的

分别考虑以 nums[0]、nums[1]、nums[2]、……、nums[i - 1] 为结尾的最长递增子序列,如果 nums[i] 能够和上述递增子序列构成更长的递增子序列,需要 nums[i] 大于上述递增子序列中的最后一个元素。又因为求的是最长递增子序列,即

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

3、初始化 dp 数组

每一个 i,对应的 dp[i](即最长递增子序列)起始大小至少都是 1,元素本身至少能构成一个长度为 1 的递增子序列

4、确定遍历顺序

根据递推公式,dp[i] 依赖于 dp[i - 1]、dp[i-2]、……、 dp[0],因此应该从左往右遍历,保证更新 dp[i] 时,被依赖的 dp 值是已被更新的正确数值

5、打印 dp 数组验证

代码如下

class Solution {
public:int lengthOfLIS(vector<int>& nums) {if (nums.size() <= 1) return nums.size();vector<int> dp(nums.size(), 1); // 全部初始化为1for (int i = 1; i < nums.size(); ++i) { // 从左向右遍历,i从1开始即可,因为dp[0]肯定为1for (int j = 0; j <= i - 1; ++j) {  // 确定dp[i]本身内层需要一个for循环,考虑所有以nums[j]为结尾的递增子序列的最大值if (nums[i] > nums[j])    // 如果nums[i]能够和递增子序列构成更长的递增子序列dp[i] = max(dp[i], dp[j] + 1);}}return *max_element(dp.begin(), dp.end());  // 注意最后的结果。最长递增子序列不一定是以dp[nums.size() - 1]结尾的}
};

674.最长连续递增序列 

力扣题目链接/文章讲解 

视频讲解 

1、确定 dp 数组下标及值含义

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

dp[i]:下标 i 表示以 nums[i] 结尾的最长连续递增序列,dp[i] 的值表示该连续序列长度的

注意该连续递增序列一定包含 nums[i] !!!

2、确定递推公式

要求 dp[i],需要考虑以 nums[i] 结尾的最长连续递增序列

因为“连续”,序列又要以 nums[i] 结尾

if (nums[i] > nums[i - 1]dp[i] = dp[i - 1] + 1;
elsedp[i] = 1;

这里就体现出和上一题的区别了

因为本题要求连续递增序列,所以就只要比较 nums[i] 与 nums[i - 1],而不用去比较 nums[j] 与nums[i](j 是在 0 到 i 之间遍历)

既然不用 j 了,那么也不用两层 for 循环,本题一层 for 循环遍历 dp 数组就行

3、dp 数组初始化

每一个 i,对应的 dp[i](即最长连续递增序列)起始大小至少都是 1,元素本身至少能构成一个长度为 1 的连续递增序列

4、确定遍历顺序

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

5、打印 dp 数组验证

代码如下

class Solution {
public:int findLengthOfLCIS(vector<int>& nums) {if (nums.size() <= 1) return nums.size();vector<int> dp(nums.size(), 1); // 初始化为1,表示以nums[i]结尾的最长连续递增序列的长度至少为1for (int i = 1; i < dp.size(); ++i) {   // 从dp[1]开始填充即可if (nums[i] > nums[i - 1])dp[i] = dp[i - 1] + 1;  // 注意“连续”条件elsedp[i] = 1;}return *max_element(dp.begin(), dp.end());  // 最长连续递增序列不一定是以dp[nums.size()-1]结尾的}
};

718.最长重复子数组 

力扣题目链接/文章讲解 

视频讲解 

本题最大的难点还是定义 dp 数组 

需要用二维数组记录两个字符串的所有比较情况 

1、确定 dp 数组下标及值的含义

dp[i][j]:nums1 中以下标 i 为结尾的某个子数组,和 nums2 中以下标 j 为结尾的某个子数组是最长重复子数组,值表示重复子数组长度为 dp[i][j] 

注意两重复子数组一定是以 nums1[i] 和 nums2[j] 结尾的 

2、确定递推公式

要推出 dp[i][j],即需要看看 nums1[i] 和 nums2[j] 是否相等

如果不相等,则以 nums1[i] 结尾的 nums1 的子数组与以 nums2[j] 结尾的 nums2 的子数组必不可能为重复子数组,即 dp[i][j] = 0

如果相等,则 dp[i][j] 的值应该为 dp[i - 1][j - 1] + 1

if (nums1[i] != nums2[j])dp[i][j] = 0;
elsedp[i][j] = dp[i - 1][j - 1] + 1;

3、dp 数组初始化

根据递推公式,需要初始化第一行和第一列,即需要初始化 dp[0][j] 和 dp[i][0]

dp[0][j]:nums1 中以下标 0 为结尾的某个子数组,和 nums2 中以下标 j 为结尾的某个子数组是最长重复子数组,值表示重复子数组长度

for (int j = 0; j < nums2.size(); ++j)
{if (nums2[j] == nums1[0])dp[0][j] = 1;elsedp[0][j] = 0;
}

dp[i][0]:nums1 中以下标 i 为结尾的某个子数组,和 nums2 中以下标 0 为结尾的某个子数组是最长重复子数组,值表示重复子数组长度 

for (int i = 0; i < nums1.size(); ++i)
{if (nums1[i] == nums2[0])dp[i][0] = 1;elsedp[i][0] = 0;
}

4、确定遍历顺序

根据递推公式,为了保证更新每一项所依赖的 dp 值已经更新,可以一层一层从左往右遍历更新

5、打印 dp 数组验证

代码如下 

class Solution {
public:int findLength(vector<int>& nums1, vector<int>& nums2) {vector<vector<int> > dp(nums1.size(), vector<int>(nums2.size()));for (int j = 0; j < nums2.size(); ++j)    // 初始化第一行{if (nums2[j] == nums1[0])dp[0][j] = 1;elsedp[0][j] = 0;}for (int i = 0; i < nums1.size(); ++i)    // 初始化第一列{if (nums1[i] == nums2[0])dp[i][0] = 1;elsedp[i][0] = 0;}for (int i = 1; i < nums1.size(); ++i)for (int j = 1; j < nums2.size(); ++j) {    // 从上往下从左往右if (nums1[i] == nums2[j])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = 0;} int result = -1;for (auto & line : dp)for (auto i : line)result = max(i, result);    // 结果为dp数组中的最大值return result;}
};

注意结果应该为 dp[i][j] 中的最大值。因为 

dp[i][j]:nums1 中以下标 i 为结尾的某个子数组,和 nums2 中以下标 j 为结尾的某个子数组是最长重复子数组,值表示重复子数组长度为 dp[i][j] 

而最终最长的重复子数组的末尾可能是对应任意的 nums1 和 nums2 中的下标 


回顾总结 

注意我们定义 dp 数组时,确定状态时,将某元素为满足条件的序列的末尾元素视为一种状态

 


http://www.ppmy.cn/news/89128.html

相关文章

【Flutter 工程】003-钩子函数:Flutter Hooks

【Flutter 工程】003-钩子函数&#xff1a;Flutter Hooks 文章目录 【Flutter 工程】003-钩子函数&#xff1a;Flutter Hooks一、概述1、前言2、Flutter Hooks 概述 二、useState 基本使用0、计数器官方 demo1、安装 flutter_hooks2、代码改造3、运行结果4、神奇的事情 三、使用…

HTTP中 Connection: keep-Alive与TCP中中keepalive有什么区别?

有小伙伴不明白keep-Alive和keepalive有什么区别&#xff1f;今天写这篇文章详细讲清楚&#xff01; HTTP是请求响应模型也就是客户端发起了请求&#xff0c;服务端才会返回响应&#xff0c;一来一回。 由于 HTTP 是基于 TCP 传输协议实现的&#xff0c;客户端与服务端要进行 H…

RESTful API介绍

RESTful API&#xff08;Representational State Transfer&#xff09;是一种设计 Web 应用程序的架构风格&#xff0c;它使用 HTTP 请求来进行数据传输和交互。 RESTful API 的核心思想是将资源&#xff08;比如用户、订单、产品&#xff09;作为 Web 上的唯一标识&#xff0…

公文写作素材:为人处世类过渡句50例

1.身处逆境&#xff0c;敢于亮剑&#xff0c;坚毅前行&#xff0c;方能逆势突围&#xff1b;面对困难&#xff0c;坚定信心&#xff0c;敢拼敢闯&#xff0c;定能笑到最后。 2.没有海纳百川的胸怀&#xff0c;怎能容得下不同性格的人&#xff1b;没有从善如流的雅量&#xff0…

【人工智能概论】 数据预处理——MaxMin归一化、Z-Score异常判断、经验异常判断

【人工智能概论】 数据预处理——MaxMin归一化、Z-Score异常判断、经验异常判断 文章目录 【人工智能概论】 数据预处理——MaxMin归一化、Z-Score异常判断、经验异常判断一. MaxMin归一化1.1 数据标准化的目的与好处1.2 归一化公式1.3 归一化代码 二. Z-Score2.1 Z-Score 原理…

CCSC认证报考攻略

CCSC认证介绍 国家计算机网络应急技术处理协调中心(CNCERT)是中央网信办公室直属事业单位&#xff0c;是“网络安全培训资助计划”发起人和技术指导单位之一。CNCERT拥有先进的实验平台、顶尖的专家团队和丰富的人才培训经验&#xff0c;熟悉政府部门、行业协会和企业事业单位…

C++ QT QDBus进阶用法。

以下是使用QDBus的高级用法示例代码&#xff1a; 1. 使用DBus的异步调用机制&#xff1a; #include <QCoreApplication> #include <QDebug> #include <QDBusConnection> #include <QDBusPendingCallWatcher> class MyDBusObject : public QObject …

IO流的讲解(2)

目录 文件拷贝 FileReader FileWriter 节点流和处理流 基本介绍 节点流和处理流的区别 处理流-BufferedReader和BufferedWriter 案例1 案例2 案例3 对于文件的拷贝&#xff0c;对于我们日常的使用来说&#xff0c;也就是复制粘贴的事情&#xff0c;但是我们如何在Jav…