DP:两个数组的dp问题

解决两个数组的dp问题的常用状态表示:

1、选取第一个字符串[0-i]区间以及第二个字符串[0,j]区间作为研究对象

2、根据题目的要求确定状态表示

字符串dp的常见技巧

1、空串是有研究意义的,引入空串可以帮助我们思考虚拟的边界如何进行初始化。

2、如果我们的dp多开了一行一列,可以在字符串的前面多加上一个空格(s=“ ”+s),这样可以保证dp数组和字符串数组的下标映射关系是一一对应的,方便我们书写代码

一、最长公共子序列(模版)

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示:s1的[0,i]区间以及s2的[0,j]区间内的所有子序列中,最长公共子序列的长度。

 2、状态转移方程

dp[i][j]:  (从最后一个位置的状况去分情况讨论)

(1)s[i]==s[j]——>dp[i-1][j-1]+1

(2)s[i]!=s[j]——>max(dp[i-1][j],dp[i][j-1])              

注意:dp[i-1][j]和dp[i][j-1]都包含了dp[i-1][j-1]的情况,但是该题只需要找最大值而不是统计个数,所以不必在意。

3、初始化

多开一行一列,引入空串去研究边界,均初始化为0即可。

4、填表顺序

从上往下填每一行

每一行从左往右填

5、返回值

dp[m][n]

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {//字符串技巧int m=text1.size(),n=text2.size();text1=" "+text1, text2=" "+text2;vector<vector<int>> dp(m+1,vector<int>(n+1));for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)if(text1[i]==text2[j]) dp[i][j]=dp[i-1][j-1]+1;else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);return dp[m][n];}
};

 二、不相交的线

. - 力扣(LeetCode)

该题其实等价于最长公共子序列 

class Solution {
public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {//相当于是最长公共子序列int m=nums1.size(),n=nums2.size();vector<vector<int>> dp(m+1,vector<int>(n+1));for(int i=1;i<=m;++i)for(int j=1;j<=n;++j) if(nums1[i-1]==nums2[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[m][n];}
};

三、不同的子序列

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示:s的[0,j]区间的所有子序列中,有多少个t的[0,i]区间内的子串

 2、状态转移方程

dp[i][j]:  (从s的子序列最后一个位置是否包含s[j]

(1)包含s[j]——>t[i]==s[j]——>+=dp[i-1][j-1]

(2)不包含s[j]——>+=dp[i][j-1]            

3、初始化

引入空串去思考:

当s和t都为空串时,应该为1

当s为空串,t不为空串的时候,应该为0

当t为空串,s不为空串的时候,应该为1

所以t为空串时,也就是i=0(第一行) 应该都初始化为1  而其他位置则是0

4、填表顺序

从上往下填每一行

每一行从左往右填

5、返回值

dp[m][n]

6、细节处理

该题可能会越界,所以用double去存储。

class Solution {
public:int numDistinct(string s, string t) {//s字符串0-j中所有子序列中 有多少个t字符串内0-i区间内的子串int m=t.size(),n=s.size();vector<vector<double>> dp(m+1,vector<double>(n+1)); //会越界 double>long long//分析最后一位的状态  t[i]==s[j] dp[i-1][j-1]//无论如何dp[i][j]+=dp[i][j-1]//可以利用空串的性质去思考for(int j=0;j<=n;++j) dp[0][j]=1; for(int i=1;i<=m;++i)for(int j=1;j<=n;++j){dp[i][j]+=dp[i][j-1];if(t[i-1]==s[j-1]) dp[i][j]+=dp[i-1][j-1];}return dp[m][n];}
};

四、通配符匹配(重点)

. - 力扣(LeetCode)

 

class Solution {
public:bool isMatch(string s, string p) {//两个数组的dp问题//p中0-j的子串能否匹配s中0-i的子串int m=s.size(),n=p.size();s=" "+s,p=" "+p;vector<vector<bool>> dp(m+1,vector<bool>(n+1));//初始化第一行 当s为空(i=0时) dp[0][0]=true;for(int j=1;j<=n;++j) if(p[j]=='*') dp[0][j]=true;else break;for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)if(p[j]=='*') dp[i][j]=dp[i-1][j]||dp[i][j-1];else dp[i][j]=(p[j]=='?'||s[i]==p[j])&&dp[i-1][j-1];return dp[m][n];}
};

 五、正则表达式匹配(重点)

. - 力扣(LeetCode)

class Solution {
public:bool isMatch(string s, string p) {//p中0-j的子串能否匹配s中0-i的子串int m=s.size(),n=p.size();s=" "+s,p=" "+p;//控制下标的映射vector<vector<bool>> dp(m+1,vector<bool>(n+1));//如果是  (p[j]=="."||p[j]==s[i])&&dp[i-1][j-1]//如果是 * 取决于p[j-1]   p[j-1]='.' 匹配空 dp[i][j-2]  匹配1个 dp[i-1][j-2]……//所以dp[i][j]=dp[i][j-2]||dp[i-1][j-2]||dp[i-2][j-2]……//数学推导 dp[i-1][j]=dp[i-1][j-2]||dp[i-2][j-2]……  //dp[i][j]=dp[i][j-2]||dp[i-1][j] //如果p[j-1]==s[i] //初始化dp[0][0]=true;for(int j=2;j<=n;j+=2) if(p[j]=='*') dp[0][j]=true;else break;//填表for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)if(p[j]=='*') dp[i][j]=dp[i][j-2]||(p[j-1]=='.'||p[j-1]==s[i])&&dp[i-1][j];else dp[i][j]=(p[j]=='.'||p[j]==s[i])&&dp[i-1][j-1];return dp[m][n];}
};

六、交错字符串

. - 力扣(LeetCode)

预处理:在s1、s2、s3前面加上“ ”

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示:s1字符串的[1,i]区间和s2字符串的[1,j]区间的字符串能否拼凑成s3[1,i+j]子串

 2、状态转移方程

dp[i][j]:  (从最后一个位置

dp[i][j]=    s1[i]==s3[i+j]&&dp[i-1][j] ||s2[j]==s3[i+j]&&dp[i][j-1]

3、初始化

引入空串去思考:

当s1和s2均为空串时,s3也为空串,所以是true

当s1是空串,s2不是空串时,s3取决于s2 所以如果第一行一直是s2[j]==s3[j]就是true,一旦有一个不满足,就跳出循环。

当s2是空串,s1不是空串时,s3取决于s1 所以如果第一列一直是s1[i]==s3[i]就是true,一旦有一个不满足,就跳出循环。

所以需要按照上面的规则对第一行和第一列进行相关的初始化。

4、填表顺序

从上往下填每一行

每一行从左往右填

5、返回值

dp[m][n]

class Solution {
public:bool isInterleave(string s1, string s2, string s3) {int m=s1.size(),n=s2.size();if(m+n!=s3.size()) return false;//优化s1=" "+s1,s2=" "+s2,s3=" "+s3;//dp[i][j]表示s1 1-i s2 1-j 能否组成 s3 1-i+jvector<vector<bool>> dp(m+1,vector<bool>(n+1));//先初始化第一列  此时s2是空串dp[0][0]=true;for(int i=1;i<=m;++i) if(s1[i]==s3[i]) dp[i][0]=true;else break;  //初始化第一行 s1是空串for(int j=1;j<=n;++j)if(s2[j]==s3[j]) dp[0][j]=true;else break;//开始填表 讨论最后一个位置  s1[i]==s[j]for(int i=1;i<=m;++i)for(int j=1;j<=n;++j) dp[i][j]=(s1[i]==s3[i+j])&&dp[i-1][j]||(s2[j]==s3[i+j])&&dp[i][j-1];return dp[m][n];}
};

七、两个字符串的最小ASCII删除和

. - 力扣(LeetCode)

 预处理:要求删除序列的最小ascii值,删除之后剩下的序列是一样的,并且总ascii值是不变的,所以我们可以运用正难则反的思路。

将问题转化为:求两个字符串所有最长公共子序列中的ascii码值的最大和

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示:s1字符串的[0,i]区间和s2字符串的[0,j]区间的所有子序列里,公共子序列最大的ascii码和

 2、状态转移方程

dp[i][j]:  (从最后一个位置

s1[i]==s2[j]——dp[i-1][j-1]+s1[i]  

s1[i]!=s2[j]——max(dp[i-1][j],dp[i][j-1])

3、初始化

引入空串去思考:

都初始化为0即可

4、填表顺序

从上往下填每一行

每一行从左往右填

5、返回值

sum-2*dp[m][n]  (sum为两个字符串的ascii码值总和)

class Solution {
public:int minimumDeleteSum(string s1, string s2) {int m=s1.size(),n=s2.size();vector<vector<int>> dp(m+1,vector<int>(n+1));//正难则反 找两个字符串的最小ascii删除和可以等价于//找两个字符串的ascii总值-两个字符串的最长公共子序列的ascii值//dp[i][j] s1 0-i 以及s2 0-j 所有子序列中最长公共子序列的ascii值总和for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)if(s1[i-1]==s2[j-1]) dp[i][j]=dp[i-1][j-1]+s1[i-1];else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);int sum=0;for(char&ch:s1) sum+=ch;for(char&ch:s2) sum+=ch;return sum-2*dp[m][n];}
};

八、最长重复子数组

. - 力扣(LeetCode)

 子数组最大的特点就是必须连续!!

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示:nums1中以i位置为结尾的所有子数组以及nums2中以j位置为结尾的所有子数组中,最长重复子数组的长度。

 2、状态转移方程

dp[i][j]:  (从最后一个位置

nums1[i]!=nums2[j]——>0

nums1[i]==nums2[j]——>dp[i-1][j-1]+1

3、初始化

都初始化为0即可

4、填表顺序

从上往下填每一行

每一行从左往右填

5、返回值

dp表中的最大值

class Solution {
public:int findLength(vector<int>& nums1, vector<int>& nums2) {int m=nums1.size(),n=nums2.size();vector<vector<int>> dp(m+1,vector<int>(n+1));//dp[i][j]表示 nums1以i位置结尾 nums2以j位置结尾的最长公共子数组长度int ret=0;for(int i=1;i<=m;++i)for(int j=1;j<=n;++j)if(nums1[i-1]==nums2[j-1]) dp[i][j]=dp[i-1][j-1]+1,ret=max(ret,dp[i][j]);return ret;}
};

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

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

相关文章

【odoo】odoo常用的ORM方法

概要 在Odoo中&#xff0c;ORM&#xff08;对象关系映射&#xff0c;Object-Relational Mapping&#xff09;方法是一种将Python对象映射到数据库表的方法。Odoo的ORM系统使开发者能够使用高级的Python代码而不是复杂的SQL语句来操作数据库。Odoo的ORM方法主要用于创建、读取、…

云服务器Ubuntu系统的vim-plus(youcompleteme)完整安装

一. 安装vim-plus PS&#xff1a;需要在那个用户下配置vim-plus&#xff0c;就到那个用户下执行代码 git clone https://github.com/chxuan/vimplus.git ~/.vimplus cd ~/.vimplus ./install.sh二. 解决没有代码自动补全的问题 随便创建一个Test.cpp文件&#xff0c;vim打开…

图像生成新篇章:Stable Diffusion 3 Medium开源评析

摘要 在数字艺术与人工智能的交汇点上&#xff0c;Stable Diffusion 3&#xff08;SD3&#xff09;的开源无疑是一场技术革新的盛宴。就在3月份&#xff0c;我撰写了一篇博文&#xff0c;深入探讨了SD3的技术报告内容与介绍&#xff0c;文章发表在CSDN博客上&#xff0c;https:…

如何用多媒体沙盘实现智能交互体验?

随着多媒体技术在内容展示领域的迅猛进步&#xff0c;智能化信息交互方式已然跃升为公众瞩目的焦点&#xff0c;而展厅作为信息传递与产品展示的核心阵地&#xff0c;正面临着提升交互体验、强化信息传递效果的迫切需求。因此&#xff0c;以多媒体沙盘、LED屏幕等创新装置为媒介…

C/C++图形渲染引擎开发方向有钱景吗?

在当前的技术环境下&#xff0c;特别是在图形渲染引擎开发领域&#xff0c;的确存在一些挑战和变化。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频 讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&#xff0c;私…

Python和OpenCV图像分块之图像边长缩小比率是2

import cv2 import numpy as npimg cv2.imread("F:\\mytupian\\xihuduanqiao.jpg") # 低反光 cv2.imshow(image, img) # # 图像分块 # dst np.zeros(img.shape, img.dtype) ratio 2 #图像边长缩小比率是2&#xff0c;也就是一张图片被分割成四份 height, wi…

证照之星是一款很受欢迎的证件照制作软件

证照之星是一款很受欢迎的证件照制作软件&#xff0c;证照之星可以为用户提供“照片旋转、裁切、调色、背景处理”等功能&#xff0c;满足用户对证件照制作的基本需求。本站证照之星下载专题为大家提供了证照之星电脑版、安卓版、个人免费版等多个版本客户端资源&#xff0c;此…

AI图书推荐:用ChatGPT按需DIY定制来赚钱

《用ChatGPT按需DIY定制来赚钱》ChatGPT Print Money Method &#xff0c;作者是Cindy Donovan 。 下面是图书概要&#xff1a; ### 第一章&#xff1a;什么是按需印刷以及ChatGPT如何使其变得简单易行&#xff1f; 本章介绍了按需印刷的商业模式&#xff0c;即仅在收到订单时…

SpringMVC-基础架构

一、什么是MVC 二、什么是SpringMVC 三、SpringMVC的特点 四、配置SpringMVC 简单流程&#xff1a; 总体框架 1.创建pom.xml依赖 <!--打包方式--><packaging>war</packaging><!--依赖--><dependencies><dependency><groupId>org.s…

使用GPT/文心实现诗词作画

在教育领域中&#xff0c;古诗词一直是培养学生文化素养和审美能力的重要载体。选择合适的古诗词进行学习和欣赏&#xff0c;不仅能够增强他们的语言表达能力&#xff0c;还能促进他们对中国传统文化的理解和热爱。本文将结合AI技术&#xff0c;将古诗词转换为图画。 1、选择适…

IT人的拖延——别让“需求沟通”耽误了你的正事

IT人的工作&#xff0c;很多“需求沟通”的场景&#xff0c;而在沟通需求时&#xff0c;又会因为沟通的不顺畅&#xff0c;没有结果而产生烦躁的情绪或者是悬而未决的不能开始行动&#xff0c;进而间接地造成了拖延。这种拖延的原因&#xff0c;需要从需求沟通的根源来找方案&a…

【云岚到家】-day03-2-门户缓存实现实战

【云岚到家】-day03-2-门户缓存实现实战 5 缓存实现5.2 定时任务更新缓存5.2.1 分布式调度平台5.2.1.1 jdk提供的Timer定时器5.2.1.2 使用第三方Quartz方式5.2.1.3 使用分布式调度平台XXL-JOB 5.2.2 XXL-JOB5.2.2.1 介绍5.2.2.2 部署调度中心5.2.2.3 执行器 5.2.2 定义缓存更新…

Kong AI Gateway 正式 GA !

Kong Gateway 3.7 版本已经重磅上线&#xff0c;我们给 AI Gateway 带来了一系列升级&#xff0c;下面是 AI Gateway 的更新亮点一览。 AI Gateway 正式 GA 在 Kong Gateway 的最新版本 3.7 中&#xff0c;我们正式宣布 Kong AI Gateway 达到了通用可用性&#xff08;GA&…

踩坑!被node-sass折磨的一天

文章目录 被node-sass折磨的一天折磨过程了解原因注意事项 被node-sass折磨的一天 折磨过程 起因是要开发一个老项目&#xff0c;照常拉代码、下依赖、启动三步走 依赖开始下载不对了&#xff0c;以为是node版本问题&#xff0c;寻找node-sass对应的node版本 利用nvm&#…

字符串的复杂操作(字符串的下标和切片、以及字符串的相关操作函数方法)

如果使用不符合标准的标识符,将会报错 SyntaxError: incalid syntax(无效语法) 文章目录 一、字符串的复杂操作1.1 下标&#xff08;也叫索引&#xff09;1.2 切片 一、字符串的复杂操作 1.1 下标&#xff08;也叫索引&#xff09; 下标代表着第几个数据&#xff0c;从0开始计…

【Css】纯css展开、收起超出的文本

效果 展开 收起 未超出 码 -webkit-line-clamp: 3; 设置限制行数 <div class"wrap"> <inputtype"checkbox"id"exp-txt"><div class"text"><labelfor"exp-txt"class"btn"></label&g…

【数组】【快慢双指针】删除有序数组中的重复项+移除元素+删除有序数组中的重复项II

今天趁热打铁&#xff0c;接着刷了几道标签是【数组】的题&#xff0c;基本都是双指针就能解决。 1、删除有序数组中的重复项 该题对应力扣网址 class Solution { public:int removeDuplicates(vector<int>& nums) {int i,j;int count 0;int n nums.size();nums.…

从零开始写 Docker(十八)---容器网络实现(下):为容器插上”网线“

本文为从零开始写 Docker 系列第十八篇&#xff0c;利用 linux 下的 Veth、Bridge、iptables 等等相关技术&#xff0c;构建容器网络模型&#xff0c;为容器插上”网线“。 完整代码见&#xff1a;https://github.com/lixd/mydocker 欢迎 Star 推荐阅读以下文章对 docker 基本实…

SwiftUI中UIViewRepresentable的使用(UIKit与SwiftUI的桥梁)

UIViewRepresentable是一个协议&#xff0c;用于创建一个SwiftUI视图&#xff0c;该视图包装了一个UIKit视图。通过实现UIViewRepresentable协议&#xff0c;我们可以在SwiftUI中使用自定义的UIKit视图&#xff0c;并与SwiftUI进行交互。 实现UIViewRepresentable 创建一个遵…

自动控制理论实验---IDFT和FFT算法的原理和MATLAB编程

1、实验设备 PC计算机1台&#xff0c;MATLAB软件1套。 2、实验目的 掌握IDFT&#xff08;逆离散傅里叶变换&#xff09;算法的原理和MATLAB编程方法。了解FFT&#xff08;快速傅里叶变换&#xff09;算法&#xff0c;并能够调用MATLAB的fft函数进行频域变换。验证IDFT程序的…