当前位置: 首页 > news >正文

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;}
};

相关文章:

DP:两个数组的dp问题

解决两个数组的dp问题的常用状态表示&#xff1a; 1、选取第一个字符串[0-i]区间以及第二个字符串[0,j]区间作为研究对象 2、根据题目的要求确定状态表示 字符串dp的常见技巧 1、空串是有研究意义的&#xff0c;引入空串可以帮助我们思考虚拟的边界如何进行初始化。 2、如…...

嵌入式Linux:格式化I/O

目录 1、格式化输出函数 1.1、printf()函数 1.2、fprintf()函数 1.3、dprintf()函数 1.4、sprintf()函数 1.5、snprintf()函数 2、格式化输入函数 2.1、scanf()函数 2.2、fscanf()函数 2.3、sscanf()函数 在Linux中&#xff0c;格式化I/O&#xff08;formatted I/O&a…...

【elementui源码解析】如何实现自动渲染md文档-第二篇

目录 1.概要 2.引用文件 1&#xff09;components.json 2&#xff09;json-template/string 3&#xff09;os.EOL 3.变量定义 4.模版填充 5.MAIN_TEMPLATE填充 6.src下的index.js文件 1&#xff09;install 2&#xff09;export 7.总结 1.概要 今天看第二个命令no…...

热门开源项目OpenHarmony

目录 1.概述 1.1.开源项目的意义 1.2.开源项目对软件行业的促进作用 1.3.小结 2.OpenHarmony 2.1.技术架构 2.2.分布式软总线 2.2.1.架构 2.2.2.代码介绍 2.2.2.1.代码目录 2.2.2.2.说明 2.2.2.3.发现组网和传输 2.2.2.3.1.发现 2.2.2.3.2.组网 2.2.2.3.3.传输…...

NewspaceAi之GPT使用新体验

GPT功能 使用地址&#xff1a;https://newspace.ai0.cn/ 上车 挂挡 踩油门&#xff0c;一脚到底&#xff0c;开始你的表演 问题1&#xff1a;你能做什么详细告诉我&#xff1f; 下面内容是GPT的回答 当然&#xff01;作为一个基于GPT-4架构的AI&#xff0c;我能够在许多方面为…...

详解红黑树

红黑树规则 节点是红色或黑色。根节点是黑色。每个叶子节点都是黑色的空节点&#xff08;NIL节点&#xff09;。每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。 红黑树…...

探索JavaScript逆向工程与风控等级

探索JavaScript逆向工程与风控等级 在当今的网络安全领域&#xff0c;JavaScript逆向工程&#xff08;简称JS逆向&#xff09;已成为许多开发者和安全专家关注的焦点。JS逆向主要涉及对JavaScript代码的分析与理解&#xff0c;以发现其内部逻辑、数据流及潜在漏洞。这种技术常用…...

C++ 22 之 立方体案例

c22立方体案例.cpp #include <iostream> #include <string>using namespace std;class Cube{ private:int cube_l; // 长int cube_w; // 宽int cube_h; // 高public:// 设置长void set_l(int l){cube_l 1;}// 设置宽void set_w(int w){cube_w w;}// 设置高void …...

vue2使用antv/g6-editor实现可拖拽流程图

依赖下载 照着这个引入就好&#xff0c;然后npm install 源码 <template><div id"vue-g6-editor"><el-row><el-col :span"24"></el-col></el-row><!-- 工具栏 --><el-row><el-col :span"24&qu…...

springboot学习小结

背景 业务上需要开发&#xff0c;组里一位前辈给我指路 spring基础 什么是spring spring提供一个容器称为spring应用上下文&#xff0c;容器里可以创建和管理组件&#xff0c;组件会在容器里装配好&#xff0c;组件也可以叫bean。 装配不由组件创建他依赖的组件&#xff0…...

vue聊天发送Emoji表情

在用web端写聊天发送表情的功能中&#xff0c;使用web端有系统自带的unicode表情会出现每端不统一的情况&#xff0c;不好用不能统一&#xff0c;在这里我想到了一个非常好的思路&#xff0c;可以解决这个问题&#xff01; 那就是发送表情用图片的形式呈现&#xff0c;然后发给…...

360数字安全:2024年4月勒索软件流行态势分析报告

勒索软件传播至今&#xff0c;360 反勒索服务已累计接收到数万勒索软件感染求助。随着新型勒索软件的快速蔓延&#xff0c;企业数据泄露风险不断上升&#xff0c;勒索金额在数百万到近亿美元的勒索案件不断出现。勒索软件给企业和个人带来的影响范围越来越广&#xff0c;危害性…...

【MySQL】日志详解

本文使用的MySQL版本是8 日志概览 它们记录了数据库系统中的不同操作和事件&#xff0c;以便于故障排除、性能优化和数据恢复。本文将介绍MySQL中常见的几种日志&#xff0c;同时也会介绍一点常用的选项。 官方文档&#xff1a;MySQL :: MySQL 8.0 Reference Manual :: 7.4 M…...

MyBatis 延迟加载,一级缓存,二级缓存设置

MyBatis不仅提供了一级缓存和二级缓存机制&#xff0c;还支持延迟加载&#xff08;Lazy Loading&#xff09;&#xff0c;以进一步优化性能。 1. 延迟加载&#xff08;Lazy Loading&#xff09; 延迟加载是在需要时才加载数据&#xff0c;而不是在查询时立即加载所有相关数据。…...

Linux 基本指令2

cp 指令 cp[选项]源文件 目标文件 将源文件的内容复制到目标文件中&#xff0c;源文件可以有多个&#xff0c;最后一个文件为目标文件&#xff0c;目标文件也可以是一段路径&#xff0c;若目的地不是一个目录的话会拷贝失败。若没有路径上的目录则会新建一个&#xff0c;若源是…...

联邦学习的基本流程,联邦学习权重聚合,联邦学习权重更新

目录 联邦学习的基本流程是 S_t = np.random.choice(range(K), m, replace=False) 联邦学习权重聚合 model.state_dict() 联邦学习权重更新 下载数据集 https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz 联邦学习的基本流程是 **1. server初始…...

React保姆级教学

React保姆级教学 一、创建第一个react项目二、JSX基本语法与react基础知识1、 插值语法&#xff1a;2、 循环一个简单列表3、 实现简单条件渲染4、 实现复杂的条件渲染5、 事件绑定6、 基础组件&#xff08;函数组件&#xff09;7、 使用useState8、 基础样式控制9、 动态类名1…...

数据结构和矩阵细节用法:double、cell和complex #matlab

矩阵建立 建立矩阵用[]&#xff1b; 矩阵的同一行内的元素用逗号或者空格隔开&#xff1b; 矩阵的不同行的元素用分号隔开 eg. 矩阵 A 1 2 3 4 5 6 7 8 9 在matlab中矩阵A表示为&#xff1a; clc;clear; A[1,2,3;4,5,6;7,8,9]; %或者A[1 2 3;4 5 …...

12. Django 第三方功能应用

12. 第三方功能应用 因为Django具有很强的可扩展性, 所以延伸了第三方功能应用. 通过本章的学习, 读者能够在网站开发过程中快速实现API接口开发, 验证码生成与使用, 站内搜索引擎, 第三方网站实现用户注册, 异步任务和定时任务, 即时通信等功能.12.1 Django Rest Framework框…...

UnityWebRequest获取本地txt文件,其中中文乱码问题(踩坑记录)

Unity获取本地streamingassert下txt文件&#xff0c;遇到点问题&#xff0c;就是用UnityWebRequest下载一个txt文件的时候&#xff0c;原txt在资源管理器用notepad打开显示正常&#xff0c;但是Unity里调试&#xff0c;打印内容却是乱码&#xff0c; 在notepad 转存为utf-8&…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...