算法沉淀——动态规划之两个数组的 dp(上)(leetcode真题剖析)

news2024/11/18 1:30:05

在这里插入图片描述

算法沉淀——动态规划之两个数组的 dp

  • 01.最长公共子序列
  • 02.不相交的线
  • 03.不同的子序列
  • 04.通配符匹配

01.最长公共子序列

题目链接:https://leetcode.cn/problems/longest-common-subsequence/

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。 

提示:

  • 1 <= text1.length, text2.length <= 1000
  • text1text2 仅由小写英文字符组成。

思路

状态转移方程:

  1. 如果当前字符相同(text1[i] == text2[j]),说明我们找到了一个新的公共字符,因此最长公共子序列的长度在之前的基础上加 1:
    • dp[i][j] = dp[i - 1][j - 1] + 1
  2. 如果当前字符不同(text1[i] != text2[j]),则我们需要考虑去掉 text1[i] 或者去掉 text2[j] 之后的最长公共子序列。这可以通过比较 text1 的前 i-1 个字符和 text2 的前 j 个字符的最长公共子序列长度,以及 text1 的前 i 个字符和 text2 的前 j-1 个字符的最长公共子序列长度,取最大值:
    • dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

初始化:为了方便处理边界情况,我们在文本 text1text2 前各添加一个空字符,即 text1[0]text2[0] 表示空串。这样,dp 表的大小为 (m+1) x (n+1),其中 mn 分别是两个文本的长度。初始化时,将第一行和第一列的值都设置为 0。

填表顺序:最终的结果存储在 dp[m][n] 中,其中 mn 是两个文本的长度。这个值表示 text1text2 的最长公共子序列的长度。

代码

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

02.不相交的线

题目链接:https://leetcode.cn/problems/uncrossed-lines/

在两条独立的水平线上按给定的顺序写下 nums1nums2 中的整数。

现在,可以绘制一些连接两个数字 nums1[i]nums2[j] 的直线,这些直线需要同时满足满足:

  • nums1[i] == nums2[j]
  • 且绘制的直线不与任何其他连线(非水平线)相交。

请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

示例 1:

输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。 
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。

示例 2:

输入:nums1 = [2,5,1,2,5], nums2 = [10,5,2,1,5,2]
输出:3

示例 3:

输入:nums1 = [1,3,7,1,7,5], nums2 = [1,9,2,5,1]
输出:2 

提示:

  • 1 <= nums1.length, nums2.length <= 500
  • 1 <= nums1[i], nums2[j] <= 2000

思路

如果我们希望确保两条直线不相交,我们可以将问题转化为在两个整数数组中寻找最长的公共子序列。这是因为,如果两条直线不相交,它们在同一列上对应的两个元素在两个数组中的顺序关系应该保持一致。

这个问题可以用动态规划来解决,具体步骤如下:

  1. 状态表达
    • 我们选取两个数组分别表示两条直线,数组长度分别为 mn
    • 定义状态表 dp,其中 dp[i][j] 表示数组1的前 i 个元素和数组2的前 j 个元素中的最长公共子序列的长度。
  2. 状态转移方程
    • 如果 nums1[i] == nums2[j],说明找到了一个公共元素,状态转移方程为 dp[i][j] = dp[i-1][j-1] + 1
    • 如果 nums1[i] != nums2[j],则需要在两个数组的前面部分分别寻找最长公共子序列,状态转移方程为 dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  3. 初始化
    • 为了方便处理边界情况,可以在两个数组的前面各添加一个虚拟元素,表示空串。
    • 初始化 dp 数组的第一行和第一列为0,因为空串与任何数组的最长公共子序列长度都为0。
  4. 填表顺序
    • 从上到下,从左到右填写 dp 数组。
  5. 返回值
    • 最终的结果存储在 dp[m][n] 中,表示两个数组的最长公共子序列的长度。

代码

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

03.不同的子序列

题目链接:https://leetcode.cn/problems/distinct-subsequences/

你两个字符串 st ,统计并返回在 s子序列t 出现的个数,结果需要对 109 + 7 取模。

示例 1:

输入:s = "rabbbit", t = "rabbit"
输出:3
解释:
如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
rabbbit
rabbbit
rabbbit

示例 2:

输入:s = "babgbag", t = "bag"
输出:5
解释:
如下所示, 有 5 种可以从 s 中得到 "bag" 的方案。 
babgbag
babgbag
babgbag
babgbag
babgbag

提示:

  • 1 <= s.length, t.length <= 1000
  • st 由英文字母组成

思路

  1. 状态表达

    • 选取第一个字符串 [0, i] 区间以及第二个字符串 [0, j] 区间作为研究对象,结合题目要求来定义状态表达。
    • 在这道题中,状态表达为 dp[i][j],表示在字符串 s[0, j] 区间内的所有子序列中,有多少个 t 字符串 [0, i] 区间内的子串。
  2. 状态转移方程

    • 根据最后一个位置的元素,结合题目要求进行分类讨论:

      • t[i] == s[j]
        

        时:

        • 选择 s[j] 作为结尾,相当于在状态 dp[i-1][j-1] 中的所有符合要求的子序列的后面,再加上一个字符 s[j],此时 dp[i][j] = dp[i-1][j-1]
        • 不选择 s[j] 作为结尾,相当于选择了状态 dp[i][j-1] 中所有符合要求的子序列,继承了上个状态里面的求得的子序列,此时 dp[i][j] = dp[i][j-1]
      • t[i] != s[j]
        

        时:

        • 子序列只能从 dp[i][j-1] 中选择所有符合要求的子序列,继承上个状态里面求得的子序列,此时 dp[i][j] = dp[i][j-1]
    • 综上,状态转移方程为:

      • 所有情况下都可以继承上一次的结果:dp[i][j] = dp[i][j-1]
      • t[i] == s[j] 时,可以多选择一种情况:dp[i][j] += dp[i-1][j-1]
  3. 初始化

    • 引入空串后,方便初始化,注意下标的映射关系以及确保后续填表是正确的。
    • s 为空时,t 的子串中有一个空串和它一样,因此初始化第一行全部为 1
  4. 填表顺序

    • 从上往下填每一行,每一行从左往右。
  5. 返回值

    • 根据状态表达,返回 dp[m][n] 的值。

代码

class Solution {
public:
    int numDistinct(string s, string t) {
        int m=t.size(),n=s.size();
        vector<vector<double>> dp(m+1,vector<double>(n+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];
    }
};

04.通配符匹配

题目链接:https://leetcode.cn/problems/wildcard-matching/

给你一个输入字符串 (s) 和一个字符模式 (p) ,请你实现一个支持 '?''*' 匹配规则的通配符匹配:

  • '?' 可以匹配任何单个字符。
  • '*' 可以匹配任意字符序列(包括空字符序列)。

判定匹配成功的充要条件是:字符模式必须能够 完全匹配 输入字符串(而不是部分匹配)。

示例 1:

输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:s = "aa", p = "*"
输出:true
解释:'*' 可以匹配任意字符串。

示例 3:

输入:s = "cb", p = "?a"
输出:false
解释:'?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。

提示:

  • 0 <= s.length, p.length <= 2000
  • s 仅由小写英文字母组成
  • p 仅由小写英文字母、'?''*' 组成

思路

在处理两个字符串之间的动态规划问题时,通常按照以下步骤进行:

  1. 状态表达

    • 选取第一个字符串 [0, i] 区间以及第二个字符串 [0, j] 区间作为研究对象,结合题目的要求定义状态表达。
    • 在这道题中,我们定义状态表达为 dp[i][j],表示字符串 p[0, j] 区间内的子串能否匹配字符串 s[0, i] 区间内的子串。
  2. 状态转移方程

    • 根据最后一个位置的元素,结合题目要求,进行分类讨论:
      • s[i] == p[j]p[j] == '?' 时,两个字符串匹配上了当前的一个字符,只能从 dp[i-1][j-1] 中看当前字符前面的两个子串是否匹配,继承上个状态中的匹配结果,dp[i][j] = dp[i][j-1]
      • p[j] == '*' 时,匹配策略有两种选择:
        • 选择 '*' 匹配空字符串,相当于它匹配了一个寂寞,直接继承状态 dp[i][j-1]dp[i][j] = dp[i][j-1]
        • 选择 '*' 向前匹配 1 ~ n 个字符,直至匹配整个 s 串。相当于从 dp[k][j-1] (0 <= k <= i) 中所有匹配情况中,选择性继承可以成功的情况,dp[i][j] = dp[k][j-1] (0 <= k <= i)。
      • p[j] 不是特殊字符且不与 s[i] 相等时,无法匹配。综上,状态转移方程为:
        • s[i] == p[j]p[j] == '?' 时:dp[i][j] = dp[i-1][j-1]
        • p[j] == '*' 时,状态转移方程优化为:dp[i][j] = dp[i-1][j] || dp[i][j-1]
  3. 初始化

    • dp 数组的值表示是否匹配,初始化整个数组为 false
    • 由于需要用到前一行和前一列的状态,初始化第一行和第一列。
      • dp[0][0] 表示两个空串是否匹配,初始化为 true
      • 第一行表示 s 为空串,p 串与空串只有一种匹配可能,即 p 串表示为 "***",此时也相当于空串匹配上空串,将所有前导为 "*"p 子串与空串的 dp 值设为 true
      • 第一列表示 p 为空串,不可能匹配上 s 串,跟随数组初始化即可。
  4. 填表顺序

    • 从上往下填每一行,每一行从左往右。
  5. 返回值

    • 根据状态表达,返回 dp[m][n] 的值。

代码

class Solution {
public:
    bool isMatch(string s, string p) {
        int m=s.size(),n=p.size();
        s=" "+s,p=" "+p;
        vector<vector<bool>> dp(m+1,vector<bool>(n+1));
        dp[0][0]=true;
        for(int i=1;i<=n;i++)
            if(p[i]=='*') dp[0][i]=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-1]||dp[i-1][j];
                else dp[i][j]=(p[j]=='?'||s[i]==p[j])&&dp[i-1][j-1];
            }

        return dp[m][n];
    }
};

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

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

相关文章

ADS-B Ground Receiver Radarcape

目录 Radarcape ADS-B MLAT Receiver Web Browser User Interface Radarcape Technical Data Radarcape Software Features Radarcape Basics Radarcape ADS-B MLAT Receiver Radarcape is a professional ADS-B receiver made for 24/7 operation. High performance rec…

2.29作业

T课上实现通信代码总结&#xff1a; 程序代码&#xff1a; TCPSER.c #include<myhead.h> #define SER_IP "192.168.244.140" //服务器IP #define SER_PORT 9999 //服务器端口号 int main(int argc, const char *argv[]) {//1.创建用于监…

踩坑之MysqlClient 安装

本以为就简单的 pip 就安装上了结果 报错 error: Microsoft Visual C 14.0 or greater is required. Get it with "Microsoft C Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/ 报错这个 让我下载 我也下载了可能没重启问题 再次安装还…

JS api基础初学

轮播图随机版 需求&#xff1a;当我们刷新页面&#xff0c;页面中的轮播图会显示不同图片以及样式 分析&#xff1a;①&#xff1a;准备一个数组对象&#xff0c;里面包含详细信息&#xff08;素材包含&#xff09; ②&#xff1a;随机选择一个数字&#xff0c;选出数组对应…

layui中,父页面与子页面,函数方法的相互调用、传参

<%--父页面--%> <script type"text/javascript">var KaoHaoType 0; // 考号类型 自定义参数1// 选取考号类型function SelectKaoHaoType(callBack) {KaoHaoType 0; // 默认选择填涂考号layer.open({type: 2, title: 请选择 考号区类型, ar…

java BIO深入学习

一、BIO的工作原理 传统Io(BIO)的本质就是面向字节流来进行数据传输的 ①:当两个进程之间进行相互通信&#xff0c;我们需要建立一个用于传输数据的管道(输入流、输出流)&#xff0c;原来我们传输数据面对的直接就是管道里面一个个字节数据的流动&#xff08;我们弄了一个 by…

靶机渗透之ConnectTheDots

对于vulnhub中的靶机&#xff0c;我们都需先下载镜像&#xff0c;然后导入VM&#xff0c;并将网络连接改为NAT模式。首先我们再来看一下靶机渗透的步骤&#xff1a;信息收集-漏洞分析-漏洞利用-提权。基本都是这个三个步骤&#xff0c;接下来开始我们今天的靶机渗透吧&#xff…

数据库系统实验

一、数据库管理系统软件的使用 1、实验概要 创建用于学生管理的数据库&#xff0c;数据库名为xsgl&#xff0c;包含学生的基本信息&#xff0c;课程信息和选课信息。数据库xsgl包含下列3个表&#xff1a; student&#xff1a;学生基本信息&#xff1b;course&#xff1a;课程…

流水账-20240229

目录 git本地回滚到到120bc409ee3b8f63a23d0060e55118bcce557acf提交记录本地提交到已有代码分支 IDEA批量导入快捷键无效更换背景主题快捷键快捷键可以设置eclipse模式&#xff0c;但是有些不生效&#xff0c;可能是冲突了Ctrl单击&#xff0c;Eclipse里面是可以跳转到代码内部…

计网 - 子网掩码的改变与内网之间通信的关系

文章目录 面试题A与B通信的全过程 面试题 想要弄清楚这个问题&#xff0c;必须了解数据包在网络上是如何传输的&#xff1a; 首先B想向A发送数据包&#xff0c;会根据双方IP来判断是否在同一子网&#xff1a; A的IP地址属于192.168.26.0/24这个网段。B的IP地址属于192.168.26.…

微信小程序构建npm失败解决方式

安装完所需要的依赖后&#xff0c;在微信开发者工具菜单栏中选择&#xff1a;“工具” -> “构建 npm”&#xff0c;但是失败。 解决方法&#xff1a;修改 project.config.json 开发者工具创建的项目&#xff0c;miniprogramRoot 默认为 miniprogram&#xff0c;package.js…

LVGL的基础知识总结

详细的内容可以参考正点原子的LVGL开发指南&#xff0c;本文只记录重点内容 正点原子后续的例程都是基于操作系统来写的 面向对象 在 LVGL 中&#xff0c;用户界面的基本构建成分是对象&#xff0c;也称为小部件&#xff0c;例如&#xff1a;按钮、标签、图片、列表、图表、文…

【Python_Zebra斑马打印机编程学习笔记(三)】解决ZPL指令无法显示中文的问题

解决ZPL指令无法显示中文的问题 解决ZPL指令无法显示中文的问题前言一、问题描述二、字符集、码表文件、字库文件1、字符集2、码表文件3、字库文件 三、两种设置中文字体的方式1、通过设置字符集、码表文件、字库文件改变默认字体2、通过^CF指令设置标准字体名称改变默认字体 解…

DOM 创建节点、添加节点和删除节点

创建元素节点 document.createElement(‘标签名’) 创建文本节点document.createTextNode ( 内容 ) 根据传入的标签名创建出一个空的元素对象创建出来的默认不显示&#xff0c;要成为别人的子元素才能显示&#xff0c;所以要结合appendChild使用 添加节点&#xff08;后面&am…

【AI Agent系列】【MetaGPT多智能体学习】4. 基于MetaGPT的Team组件开发你的第一个智能体团队

本系列文章跟随《MetaGPT多智能体课程》&#xff08;https://github.com/datawhalechina/hugging-multi-agent&#xff09;&#xff0c;深入理解并实践多智能体系统的开发。 本文为该课程的第四章&#xff08;多智能体开发&#xff09;的第二篇笔记。主要是对MetaGPT中Team组件…

每日一练:LeeCode-701、二叉搜索树中的插入操作【二叉搜索树+DFS+全搜】

本文是力扣 每日一练&#xff1a;LeeCode-701、二叉搜索树中的插入操作【二叉搜索树DFS全搜】学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode。 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和要插入树中的值 …

机器学习图像识别如何处理标签以外的图像?

机器学习图像识别技术是一种基于人工智能的图像处理方法&#xff0c;它通过训练大量的图像数据集来让计算机学习如何识别和分类图像。在图像识别任务中&#xff0c;我们通常需要对图像进行标注和分类&#xff0c;以便让计算机能够从中学习。但是&#xff0c;有时候我们可能会遇…

减少页面加载时间:提升用户体验的关键

✨✨ 祝屏幕前的您天天开心&#xff0c;每天都有好运相伴。我们一起加油&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、为什么页面加载时间重要&#xff1f; 二、如何减少页面加载时间&#xff1f; …

集中和离散

数据分类&#xff1a;定性(分类&#xff0c;顺序)&#xff0c;定量(数值) 分类&#xff1a;男&#xff0c;女 顺序&#xff1a;高&#xff0c;中&#xff0c;低 数值&#xff1a;可计算的数字 数据等级&#xff1a;分类<顺序<数值。高级数据可以用低级数据&#xff0c…

day09_面向对象_构造方法_封装

今日内容 零、 复习昨日 一、构造方法 二、重载 三、封装 零、 复习昨日 1 类和对象是什么关系? 类是模板(原材料)对象是具体实例(成品)类创建出对象 2 类中有什么?(类的成员) 成员属性(成员变量), 成员方法 3 创建对象的语法? 类名 对象名 new 类名(); 4 调用对象属性,方法…