DP:两个数组的dp问题

news2025/6/27 0:15:08

解决两个数组的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+j
         vector<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.coloradmin.cn/o/1822698.html

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

相关文章

Python数据分析与机器学习在医疗诊断中的应用

文章目录 &#x1f4d1;引言一、数据收集与预处理1.1 数据收集1.2 数据预处理 二、特征选择与构建2.1 特征选择2.2 特征构建 三、模型选择与训练3.1 逻辑回归3.2 随机森林3.3 深度学习 四、模型评估与调优4.1 交叉验证4.2 超参数调优 五、模型部署与应用5.1 模型保存与加载5.2 …

深入解析Prometheus:强大的开源监控与告警系统

目录 引言 一、运维监控平台的设计思路 &#xff08;一&#xff09;设计思路 1.数据收集模块 2.数据提取模块 3.监控告警模块 &#xff08;二&#xff09;监控平台层级 二、Prometheus简介 &#xff08;一&#xff09;基本介绍 &#xff08;二&#xff09;核心特征 …

二叉树左右树交换

leetcode 226题 翻转二叉树 题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3]…

聚道云软件连接器:企业数字化转型新动力

在当今数字化浪潮中&#xff0c;企业如何高效整合内部资源、优化业务流程、提升客户满意度&#xff0c;已成为每个企业亟需解决的问题。该公司作为行业内的佼佼者&#xff0c;近期借助聚道云软件连接器成功实现了飞鱼CRM与金蝶云星辰的对接&#xff0c;开启了数字化转型的新篇章…

探索uni-app x:下一代跨平台应用开发引擎

摘要 随着移动互联网的快速发展&#xff0c;跨平台应用开发的需求日益旺盛。传统的原生开发虽然性能卓越&#xff0c;但开发周期长、维护成本高。而Web应用开发虽然开发效率高&#xff0c;但性能往往不尽如人意。在这样的背景下&#xff0c;uni-app x应运而生&#xff0c;作为…

Go Module详解

文章目录 基本介绍相关环境变量Go Module的使用初始化项目&#xff08;go mod init&#xff09;管理依赖项&#xff08;go mod edit&#xff09;获取依赖项&#xff08;go mod download&#xff09;整理依赖项&#xff08;go mod tidy&#xff09;导入vendor目录&#xff08;go…

单片机建立自己的库文件(3)

文章目录 前言一、新建工程二、将库文件复制到工程中1.在工程中添加.c文件2.主函数中添加LCD1602.h文件3.主函数中添加需要的LCD1602的显示文件 三、编译测试四、遇到问题五、最后完整.h .c总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 项目需要…

从“数据孤岛”、Data Fabric(数据编织)谈逻辑数据平台

提到逻辑数据平台&#xff0c;其核心在于“逻辑”&#xff0c;与之相对的便是“物理”。在过去&#xff0c;为了更好地利用和管理数据&#xff0c;我们通常会选择搭建数据仓库和数据湖&#xff0c;将所有数据物理集中起来。但随着数据量、用数需求和用数人员的持续激增&#xf…

细说ARM MCU的串口发送数据的实现过程

目录 1、条件及工程配置 2、实现串口发送的库函数 3、修改whlie(1)中的代码 4、修改回调函数 5、下载运行 前面的文章介绍了用串口的接收中断来接收数据&#xff0c;本文介绍通过串口从MCU向外发送数据。 1、条件及工程配置 文章依赖的硬件及工程配置同本文作者的其他文…

vscode插件开发之 - menu配置

上一遍博客介绍了如何从0到1搭建vscode插件开发的base code&#xff0c;这遍博客将重点介绍如何配置menu。通常&#xff0c;开发一款插件&#xff0c;会将插件显示在VSCode 左侧的活动栏&#xff08;Activity Bar&#xff09;&#xff0c;那么如何配置让插件显示在Activity Bar…

利用Morph Studio平台免费生成AI视频教程和效果体验

今天体验一下生成AI视频平台&#xff0c;目前是免费的&#xff0c;但生成效果还是不错的,可以根据输入文字&#xff0c;或者上传图片&#xff0c;或者上传视频来自动生成视频。 访问官网&#xff0c;登录之后点击“create Library” &#xff0c;比如我建了一个“AI视频”的Li…

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

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

代码随想录算法训练营第36期DAY58

DAY58 今天的主题是&#xff1a;编辑距离。在字符串进行增删字符的操作。 392判断子序列&#xff0c;简单 首先想到快慢双指针&#xff1a; 通过了&#xff0c;很好&#xff1a; class Solution {public: bool isSubsequence(string s, string t) { int slow0; …

红酒保存中的氧气管理:适度接触与避免过度氧化

在保存云仓酒庄雷盛红酒的过程中&#xff0c;我们不得不面对一个微妙的问题&#xff1a;氧气管理。氧气&#xff0c;这个我们生活中无处不在的气体&#xff0c;对于红酒的保存却有着至关重要的影响。适度接触氧气对红酒的陈年过程和品质维护具有积极作用&#xff0c;然而过度氧…

修改eclipse ide的类及console的字体

查看了一下&#xff0c;这个类的字体看的很不爽&#xff0c;下面的是设置好的界面&#xff1a; Window -- Preferences--General--Appearance--Basic--Text Font Debug -- Console Font 经过以上的设置&#xff0c;就可以了。

java面试整合全套

什么是Java &#xff08;定义 优点&#xff09; java是一个平台&#xff0c;由jvm和Java应用编程接口构成的一门面向编程语言。 不仅吸收了C语言的各种优点&#xff0c;还摒弃了c语言里面的多继承,指针等概念&#xff0c;因此java的特征主要有功能强大和简单易用的特征。 jav…

【2.4GHz数据通信芯片解读】:Ci24R1与Si24R1有何不同?

开头我想先跟大家聊聊对2.4GHz无线射频芯片的看法&#xff0c;其中关于2.4GHz有源在整个物联网应用中是感知层无法或缺的一环&#xff0c;尤其是在一些无法通电的场所&#xff0c;可以为相对应的物联网方案赋能。 而在2.4GHz数据通信芯片里面&#xff0c;Ci24R1与Si24R1都具备收…

如何查找您的 SOLIDWORKS 序列号或许可证密钥

每个 SOLIDWORKS正版的软件都有自己的许可密钥&#xff0c;也称之为SOLIDWOKS的序列号。硕迪科技作为SOLIDKS正版软件代理商&#xff0c;我们的技术团队经常帮助客户查找他们的序列号。这篇文章将向您展示如何查找您的 SOLIDWORKS 序列号。 如果您拥有独立的 SOLIDWORKS 许可&…

使用QT制作QQ登录界面

mywidget.cpp #include "mywidget.h"Mywidget::Mywidget(QWidget *parent): QWidget(parent) {/********制作一个QQ登录界面*********************/this->resize(535,415);//设置登录窗口大小this->setFixedSize(535,415);//固定窗口大小this->setWindowTi…

【数据库设计】宠物商店管理系统

目录 &#x1f30a;1 问题的提出 &#x1f30a;2 需求分析 &#x1f30d;2.1 系统目的 &#x1f30d;2.2 用户需求 &#x1f33b;2.2.1 我国宠物行业作为新兴市场&#xff0c;潜力巨大 &#x1f33b;2.2.2 我国宠物产品消费规模逐年增大 &#x1f33b;2.2.3 我国宠物主选…