Day53【动态规划】1143.最长公共子序列、1035.不相交的线、53.最大子序和

news/2024/11/15 19:57:48/

1143.最长公共子序列

力扣题目链接/文章讲解

视频讲解

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

本题和718.最长重复子数组区别在于这里不要求是连续的了,但要有相对顺序

直接动态规划五部曲!

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

dp[i][j]:取 text1 中下标 [0, i - 1] 的子字符串与 text2 中下标为 [0, j - 1] 的子字符串,dp[i][j] 的值表示这两个子字符串的最长公共子序列长度为 dp[i][j]

2、确定递推公式

主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1] 不相同

注意不要求连续

  • 如果 text1[i - 1] 与 text2[j - 1] 相同,那么找到了一个公共元素,所以 dp[i][j] = dp[i - 1][j - 1] + 1
  • 如果 text1[i - 1] 与 text2[j - 1] 不相同,则 text1[0, i - 1] 与 text2[0, j - 1] 的最长公共子序列长度一定为 text1[0, i - 2] 与 text2[0, j - 1] 的最长公共子序列长度或 text1[0, i - 1] 与 text2[0, j - 2] 的最长公共子序列长度之一,取最大的

即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 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]);
}

3、dp 数组初始化

需要初始化第一列和第一行 dp 数组

先看看 dp[i][0] 应该是多少呢?

test1[0, i-1] 和空串的最长公共子序列自然是 0,所以 dp[i][0] = 0

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

其他下标都是随着递推公式逐步覆盖,初始为多少都可以

4、确定遍历顺序

从递推公式,可以看出,有三个方向可以推出dp[i][j],如图 

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

5、打印 dp 数组验证

代码如下

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;elsedp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);}}return dp[text1.size()][text2.size()];}
};

这里,定义 dp 数组为取 text1 中下标 [0, i - 1] 的子字符串与 text2 中下标为 [0, j - 1] 的子字符串,dp[i][j] 的值表示这两个子字符串的最长公共子序列长度为 dp[i][j]

这里的 i - 1 是为了方便初始化 

我们也可以如下定义: 定义 dp 数组为取 text1 中下标 [0, i] 的子字符串与 text2 中下标为 [0, j] 的子字符串,dp[i][j] 的值表示这两个子字符串的最长公共子序列长度为 dp[i][j]

这样我们的代码在初始化部分会复杂一点

代码及注释如下 

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {// 1、定义dp数组下标及含义// dp[i][j]表示text1[0, i]与text2[0, j]这两个子串的最长公共子序列的长度vector<vector<int> > dp(text1.size(), vector<int>(text2.size(), 1));// 2、确定递推公式:考虑text1[i]与text2[j]是否相同// 如果相同,则dp[i][j] = dp[i-1][j-1]+1,即text1[0,i-1]与text2[0, j-1]这两子串的最长公共子序列长度+1// 如果不相同,则dp[i][j]一定为text1[0, i-1]与text2[0, j]的最长公共子序列长度或text1[0, i]与text2[0, j-1]的最长公共子序列长度之一,取最大的// 3、dp数组初始化,需要初始化第一行和第一列for (int j = 0; j < text2.size(); ++j) {    // 初始化第一行// dp[0][j]表示text1[0]与text2[0, j]的最长公共子序列长度,如果text2[0, j]包含text1[0],则为1,否则为0if (text2[j] == text1[0])break;  // 如果遍历到满足条件的了,则当前包括后面的text2[0, j]一定包含text1[0]了,就为1dp[0][j] = 0;   // 否则说明当前串text2[0, j]一定不含text1[0]}for (int i = 0; i < text1.size(); ++i) {    // 初始化第一列if (text1[i] == text2[0])break;dp[i][0] = 0;}// 4、确定遍历顺序:从前向后从上向下遍历填充for (int i = 1; i < text1.size(); ++i)for (int j = 1; j < text2.size(); ++j)if (text1[i] == text2[j])dp[i][j] = dp[i - 1][j - 1] + 1;elsedp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);// 5、打印dp数组验证return dp[text1.size() - 1][text2.size() - 1];}
};

1035.不相交的线

力扣题目链接/文章讲解

视频讲解

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

和上一道题一模一样

直接上代码

class Solution {
public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {// dp[i][j]表示nums1[0, i]与nums2[0, j]这两个子数组的最长公共子序列的长度vector<vector<int> > dp(nums1.size(), vector<int>(nums2.size(), 1));for (int j = 0; j < nums2.size(); ++j) {if (nums2[j] == nums1[0])   break;dp[0][j] = 0;}for (int i = 0; i < nums1.size(); ++i) {if (nums1[i] == nums2[0])   break;dp[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] = max(dp[i-1][j], dp[i][j-1]);}return dp[nums1.size()-1][nums2.size()-1];}
};

53.最大子数组和

力扣题目链接/文章讲解 

视频讲解 

本题可以用贪心算法,也可以用动态规划

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

dp[i]:下标 i 表示以 nums[i] 为结尾的有最大和的连续子数组,值表示该子数组和

注意 nums[i] 一定是有着最大和的连续子数组中的最后一个元素 

2、确定递推公式

dp[i] 只有两个方向可以推出来:

  • dp[i - 1] + nums[i],即:nums[i] 加入当前以 nums[i-1] 为结尾的连续子序列和
  • nums[i],即:从头开始计算当前连续子序列和

一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i])

3、dp 数组初始化

从递推公式可以看出来 dp[i] 是依赖于 dp[i - 1] 的状态,dp[0] 就是递推公式的基础 

根据 dp 下标及值含义:dp[0] = nums[0]

4、确定遍历顺序

递推公式中 dp[i] 依赖于 dp[i - 1] 的状态,需要从前向后遍历,保证被依赖的 dp 值是已被更新后的正确值

5、打印 dp 数组验证

代码如下

class Solution {
public:int maxSubArray(vector<int>& nums) {// 确定dp数组下标及值含义:i表示以nums[i]为结尾的有最大和的子数组,dp[i]的值表示该最大子数组和vector<int> dp(nums.size());// 递推公式:要么将nums[i]加入具有最大和的子数组,要么从nums[i]重新开始统计具有最大和的子数组// 初始化dp[0]dp[0] = nums[0];// 从左向右遍历填充dpfor (int i = 1; i < nums.size(); ++i)dp[i] = max(dp[i - 1] + nums[i], nums[i]);return *max_element(dp.begin(), dp.end());}
};

回顾一下 dp[i] 的定义:下标 i 表示以 nums[i] 为结尾的有最大和的连续子数组

那么我们要找有最大和的子数组,就应该找每一个 nums[i] 为终点的有最大和的子数组


回顾总结 

操作两个序列需要二维 dp

还是定义 dp 数组是关键

 


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

相关文章

纯js实现在线文字识别,从图片中提取文本信息

当你需要将图片中的文字内容提取出来时&#xff0c;你可能想到了手动输入或者使用OCR技术。而当你需要进行在线文字识别时&#xff0c;一个纯JavaScript实现的OCR工具可能会成为你的优选方案。 纯JavaScript&#xff0c;使得在浏览器内部进行文字识别变得可能。 此外&#x…

python+vue空巢老人网上药店购药系统9h2k5

本空巢老人购药系统主要包括三大功能模块&#xff0c;即用户功能模块、家属功能模块和管理员功能模块。 &#xff08;1&#xff09;管理员模块&#xff1a;系统中的核心用户是管理员&#xff0c;管理员登录后&#xff0c;通过管理员功能来管理后台系统。主要功能有&#xff1a;…

Net跨平台UI框架Avalonia入门-DataGrid的使用

Avalonia中的DataGrid的使用 DataGrid 数据表格是客户端UI中很重要的一个控件&#xff0c;Avalonia中的DataGrid是单独一个包Avalonia.Controls.DataGrid&#xff0c;要使用DataGrid&#xff0c;需要另外在Nuget中按这个包&#xff0c;下面介绍一下DataGrid的安装和使用 Data…

恭喜,拿到华为OD offer了,并分享刷题经验

目录 一、先介绍一下华为OD是个什么鬼&#xff1f;1、华为OD背景2、华为OD现状3、华为OD趋势4、华为OD介绍&#xff08;1&#xff09;人力外包&#xff08;2&#xff09;员工关系&#xff08;3&#xff09;工作关系&#xff08;4&#xff09;薪酬福利&#xff08;5&#xff09;…

Golang每日一练(leetDay0075) 打家劫舍II、最短回文串

目录 213. 打家劫舍 II House Robber ii &#x1f31f;&#x1f31f; 214. 最短回文串 Shortest Palindrome &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C…

MySQL 主从复制与读写分离

MySQL 主从复制与读写分离 1、什么是读写分离&#xff1f; 读写分离&#xff0c;基本的原理是让主数据库处理事务性增、改、删操作&#xff08;INSERT、UPDATE、DELETE&#xff09;&#xff0c;而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群…

3D图像双线性插值

文章目录 前言结论说明&#xff1a;公式 测试 前言 看了一下2d图像的双线性插值的理论&#xff0c;基本上都是在原图上找到对应的浮点坐标 p f p_f pf​后&#xff0c;将以 p f p_f pf​外围的4个点进行计算。计算的方法类似于二维直线方程的理论&#xff0c;但是写成了权重的…

STL-常用算法(一.遍历 查找 排序)

目录 常用遍历算法&#xff1a; for_each和transform函数示例&#xff1a; 常用查找算法&#xff1a; find函数示例&#xff1a; find_if函数示例&#xff1a; adjacent_find示例&#xff1a; binary_search函数示例&#xff1a; count函数示例&#xff1a; count_if函…