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

在这里插入图片描述

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

  • 01.正则表达式匹配
  • 02.交错字符串
  • 03.两个字符串的最小ASCII删除和
  • 04.最长重复子数组

01.正则表达式匹配

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

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。

  • '.' 匹配任意单个字符
  • '*' 匹配零个或多个前面的那一个元素

所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例 1:

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

示例 2:

输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。 

提示:

  • 1 <= s.length <= 20
  • 1 <= p.length <= 20
  • s 只包含从 a-z 的小写字母。
  • p 只包含从 a-z 的小写字母,以及字符 .*
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

思路

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

  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-1][j-1]
      • p[j] == '*' 时,匹配策略有两种选择:
        • 一种选择是:p[j-1]* 匹配空字符串,相当于这两个字符都匹配了一个寂寞,直接继承状态 dp[i][j-2]dp[i][j] = dp[i][j-2]
        • 另一种选择是:p[j-1]* 向前匹配 1 ~ n 个字符,直至匹配上整个 s 串。相当于从 dp[k][j-2] (0 < k <= i 且 s[k]~s[i] = p[j-1]) 中所有匹配情况中,选择性继承可以成功的情况,dp[i][j] = dp[k][j-2] (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][j-2] || dp[i-1][j]
  3. 初始化

    • dp 数组的值表示是否匹配,初始化整个数组为 false
    • 由于需要用到前一行和前一列的状态,初始化第一行和第一列。
      • dp[0][0] 表示两个空串是否匹配,初始化为 true
      • 第一行表示 s 为空串, 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=2;i<=n;i+=2)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-2]||(p[j-1]=='.'||p[j-1]==s[i])&&dp[i-1][j];else dp[i][j]=(p[j]==s[i]||p[j]=='.')&&dp[i-1][j-1];}return dp[m][n];}
};

02.交错字符串

题目链接:https://leetcode.cn/problems/interleaving-string/

给定三个字符串 s1s2s3,请你帮忙验证 s3 是否是由 s1s2 交错 组成的。

两个字符串 st 交错 的定义与过程如下,其中每个字符串都会被分割成若干 非空 子字符串:

  • s = s1 + s2 + ... + sn
  • t = t1 + t2 + ... + tm
  • |n - m| <= 1
  • 交错s1 + t1 + s2 + t2 + s3 + t3 + ... 或者 t1 + s1 + t2 + s2 + t3 + s3 + ...

注意:a + b 意味着字符串 ab 连接。

示例 1:

输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
输出:true

示例 2:

输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
输出:false

示例 3:

输入:s1 = "", s2 = "", s3 = ""
输出:true

提示:

  • 0 <= s1.length, s2.length <= 100
  • 0 <= s3.length <= 200
  • s1s2、和 s3 都由小写英文字母组成

思路

  1. 状态表达
    • 对于两个字符串的动态规划问题,首先考虑选取第一个字符串的 [0, i] 区间和第二个字符串的 [0, j] 区间作为研究对象。
    • 定义状态表达 dp[i][j],表示字符串 s1[1, i] 区间内的字符和字符串 s2[1, j] 区间内的字符是否能够交错组成字符串 s3[1, i + j] 区间内的字符。
  2. 状态转移方程
    • 根据两个区间上的最后一个字符,进行分类讨论:
      • 如果 s3[i + j] = s1[i],说明交错后的字符串的最后一个字符和 s1 的最后一个字符匹配了。这时,需要判断整个字符串是否能够交错组成,即 dp[i][j] = dp[i - 1][j]
      • 如果 s3[i + j] = s2[j],说明交错后的字符串的最后一个字符和 s2 的最后一个字符匹配了。这时,需要判断整个字符串是否能够交错组成,即 dp[i][j] = dp[i][j - 1]
      • 如果两者的末尾字符都不等于 s3 最后一个位置的字符,说明不可能是两者的交错字符串,dp[i][j] 保持不变。
  3. 初始化
    • 初始化第一个位置,dp[0][0] = true,因为空串与空串能够构成空串。
    • 初始化第一行,dp[0][j],表示 s1 是空串,需要判断与 s2 的交错情况。如果 s2[j - 1] == s3[j - 1]dp[0][j - 1] 为真,则 dp[0][j] = true
    • 初始化第一列,dp[i][0],表示 s2 是空串,需要判断与 s1 的交错情况。如果 s1[i - 1] == s3[i - 1]dp[i - 1][0] 为真,则 dp[i][0] = true
  4. 填表顺序
    • 从上往下逐行填表,每一行从左往右。
  5. 返回值
    • 根据状态表达 dp[m][n] 的值,其中 mn 分别是 s1s2 的长度,判断是否能够交错组成字符串 s3

代码

class Solution {
public:bool isInterleave(string s1, string s2, string s3) {int m=s1.size(),n=s2.size();if(s3.size()!=m+n) return false;s1=" "+s1,s2=" "+s2,s3=" "+s3;vector<vector<bool>> dp(m+1,vector<bool>(n+1));dp[0][0]=true;for(int i=1;i<=n;++i) if(s2[i]==s3[i]) dp[0][i] = true;else break;for(int i=1;i<=m;++i)if(s1[i]==s3[i]) dp[i][0] = true;else break;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];}
};

03.两个字符串的最小ASCII删除和

题目链接:https://leetcode.cn/problems/minimum-ascii-delete-sum-for-two-strings/

给定两个字符串s1s2,返回 使两个字符串相等所需删除字符的 ASCII 值的最小和

示例 1:

输入: s1 = "sea", s2 = "eat"
输出: 231
解释: 在 "sea" 中删除 "s" 并将 "s" 的值(115)加入总和。
在 "eat" 中删除 "t" 并将 116 加入总和。
结束时,两个字符串相等,115 + 116 = 231 就是符合条件的最小和。

示例 2:

输入: s1 = "delete", s2 = "leet"
输出: 403
解释: 在 "delete" 中删除 "dee" 字符串变成 "let",
将 100[d]+101[e]+101[e] 加入总和。在 "leet" 中删除 "e" 将 101[e] 加入总和。
结束时,两个字符串都等于 "let",结果即为 100+101+101+101 = 403 。
如果改为将两个字符串转换为 "lee" 或 "eet",我们会得到 433 或 417 的结果,比答案更大。

提示:

  • 0 <= s1.length, s2.length <= 1000
  • s1s2 由小写英文字母组成

思路

  1. 状态表达

    • dp[i][j] 表示字符串 s1[0, i] 区间以及字符串 s2[0, j] 区间内的所有的子序列中,公共子序列的 ASCII 最大和。
  2. 状态转移方程

    • 根据最后一个位置的元素,进行情况讨论:

      • 如果 s1[i] == s2[j],说明当前字符可以被加入到公共子序列中,此时 dp[i][j] = dp[i - 1][j - 1] + s1[i]

      • 如果

        s1[i] != s2[j]
        

        ,此时有三种可能:

        • s1[0, i - 1] 区间以及 s2[0, j] 区间内找公共子序列的最大和,即 dp[i][j] = dp[i - 1][j]
        • s1[0, i] 区间以及 s2[0, j - 1] 区间内找公共子序列的最大和,即 dp[i][j] = dp[i][j - 1]
        • s1[0, i - 1] 区间以及 s2[0, j - 1] 区间内找公共子序列的最大和,即 dp[i][j] = dp[i - 1][j - 1]
      • 由于前两种情况包含了第三种情况,因此只需考虑前两种情况下的最大值。

  3. 初始化

    • 引入空串后,扩大了状态表的规模,方便初始化。
    • 需要注意下标的映射关系以及确保后续填表的正确性。
    • s1s2 为空时,没有长度,所以第一行和第一列的值初始化为 0。
  4. 填表顺序

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

    • 先找到 dp[m][n],即最大公共 ASCII 和。
    • 统计两个字符串的 ASCII 码和 sum
    • 返回 sum - 2 * dp[m][n]

代码

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));for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){dp[i][j]=max(dp[i][j-1],dp[i-1][j]);if(s1[i-1]==s2[j-1]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+s1[i-1]);}int sum=0;for(auto s:s1) sum+=s;for(auto s:s2) sum+=s;return sum-dp[m][n]*2;}
};

04.最长重复子数组

题目链接:https://leetcode.cn/problems/maximum-length-of-repeated-subarray/

给两个整数数组 nums1nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度

示例 1:

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。

示例 2:

输入:nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0]
输出:5

提示:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 100

思路

  1. 状态表达
    • dp[i][j] 表示以第一个数组的第 i 位置为结尾以及第二个数组的第 j 位置为结尾时,两个数组的最长重复子数组的长度。
  2. 状态转移方程
    • nums1[i] == nums2[j] 时,说明当前位置两个数组的元素相等,此时最长重复子数组的长度应该等于 1 加上除去最后一个位置时,以 i - 1, j - 1 为结尾的最长重复子数组的长度,即 dp[i][j] = 1 + dp[i - 1][j - 1]
  3. 初始化
    • 为了处理越界的情况,添加了一行和一列,使 dp 数组的下标从 1 开始。
    • 第一行表示第一个数组为空,因此没有重复子数组,所以其中的值设置为 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));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/news/709338.shtml

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

相关文章

产品经理岗位的任职资格和职业规划

产品经理主要是商业银行以客户为导向的&#xff0c;具体负责组织银行某一金融产品线的创新设计、生产营销和管理服务的工作。这类人士主要负责应用实施工作&#xff0c;其中产品线由一系列的产品构成&#xff0c;公司的产品经理主要分为全过程产品创新设计专家、全过程产品生产…

武汉灰京文化:跨平台和云游戏,手游行业的未来发展趋势

随着科技的不断进步和智能设备的普及&#xff0c;手游行业正迎来新的发展机遇。武汉灰京文化认为&#xff0c;在这个过程中&#xff0c;跨平台游戏和云游戏作为两大关键技术将推动手游行业迈向更加丰富、便捷和多元化的发展。 跨平台游戏的出现将为玩家带来全新的游戏体验。在…

java常用环境docker安装

配置目录 rocketmqredismysql不配置binlog配置binlog Nacoszookeeper 本文为精简安装&#xff0c;部分不带容器卷映射&#xff0c;仅供以学习使用。 rocketmq nameservice sudo docker run -d \ --privilegedtrue \ --name rmqnamesrv \ -p 9876:9876 \ -e "MAX_HEAP_SI…

visio、ppt、office等另存图片,如何设置更清晰

visio、ppt、office等另存图片&#xff0c;如何设置更清晰 选中要另存为的部分——文件——另存为——选好位置——格式选jpg——保存——按下图设置&#xff1a;质量100%&#xff0c;分辨率选打印机&#xff0c;大小选屏幕——确定

android开发前景2019,android高级面试framework

到底是公司养活了我&#xff0c;还是我养活了公司&#xff1f; 1. 很难在一家公司干到退休 在我父母那一代&#xff0c;一个上班的职工&#xff0c;往往可以在一家单位干到退休&#xff0c;名副其实的“铁饭碗”。甚至更早之前的年代&#xff0c;职工的子女还可以接父母的班&a…

计算机专业大学四年应该如何规划(Java方向)

计算机专业的学生&#xff0c;如何在大学四年内提高自己的竞争力&#xff0c;毕业之后直接进大厂工作&#xff1f; 以下将从大学四年计算机专业的学习规划、课程设置、能力提升、参考书籍等方面&#xff0c;为同学们提供一些建议和指导。 大一&#xff1a; 主攻技能学习并且达…

数据结构之数组

一、定义 数组&#xff08;Array&#xff09;是一种用连续的内存空间存储相同数据类型数据的线性数据结构。 二、内存结构 1.创建数组 我们创建一个数组 int[] array {22,33,88,66,55,25} &#xff0c;在内存结构如下图所示&#xff1a; 首先创建了array数组&#xff0c;会…

Node.js基础---npm与包

包 概念&#xff1a;Node.js 中的第三方模块又叫做包 来源&#xff1a;由第三方个人或团队开发出来的&#xff0c;免费使用&#xff0c;且为开源 为什么需要&#xff1a;Node.js的内置模块只有一些底层API&#xff0c;开发效率低 包是基于内置模块封装出来的&#xff0c;提供更…

python实现跨进程(跨py文件)通信01

前言 项目中总会遇到数据需要跨进程通信的问题&#xff0c;今天就给大家带来一套简单的跨进程通信代码。代码分为服务端与客户端两部分。 一、server端 import multiprocessing import timedef do_socket(conn, addr, ):try:while True:if conn.poll(1) False:time.sleep(0…

ZCANPRO基础操作流程

硬件准备 测试单关节需要准备如下工具&#xff1a; 电源&#xff1a; 推荐使用20-27V直流电源。关节峰值功率为额定功率的三倍。 CAN卡&#xff1a; 推荐使用周立功USB转CANFD卡&#xff0c;我们的单关节测试软件适配了该型号CAN卡驱动。 WHJ系列关节模组 WHJ系列关节模组包含…

2024最新大厂Android面试真题解析,三年老Android经验面经

前言 不知道大家面试的时候&#xff0c;有没有遇到这种情况&#xff0c;面试工资谈的是10K&#xff0c;最后干着40K的活&#xff01;说着冠冕堂皇&#xff0c;提升大家能力的话&#xff0c;做着死命压榨员工&#xff0c;996成了程序员心里的魔咒&#xff01; 初级安卓开发工程…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的活体人脸检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;本篇博客详细讲述了如何利用深度学习构建一个活体人脸检测系统&#xff0c;并且提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并进行了与前代算法YOLOv7、YOLOv6、YOLOv5的细致对比&#xff0c;展示了其在图像、视频、实时视频流和批量文件处…

CSS_实现三角形和聊天气泡框

如何用css画出一个三角形 1、第一步 写一个正常的盒子模型&#xff0c;先给个正方形的div&#xff0c;便于观察&#xff0c;给div设置宽高和背景颜色 <body><div class"box"></div> </body> <style>.box {width: 100px;height: 100px…

常用git 打tag命令

1.查看所有tag git tag 2.创建 v5.0.0的tag git tag v5.0.0 git tag &#xff08;创建后查看&#xff09; 3.推送到远程tag git push origin v5.0.0 4.删除远程tag git push origin --delete v5.0.0 5.删除本地tag git tag -d v5.0.0 6.添加带有备注信息的tag git tag v5.…

Java反射机制底层原理

反射机制 这篇文章我是参考了Java 中的反射机制&#xff08;两万字超全详解&#xff09;_java反射-CSDN博客 然后我在这里做一下总结&#xff0c;因为原文章真的很好&#xff0c;我才疏学浅没什么进行补充&#xff0c;只能做出自己的总结并且写一下自己对这个的理解。 原理&…

配置artifactory的反向代理和域名访问

一、概述 在许多情况下&#xff0c;组织会通过反向代理来提供对 Artifactory 的访问。在某些情况下&#xff0c;例如使用 Artifactory 作为 Docker 注册表&#xff0c;这种设置甚至是强制性的。为了简化反向代理的配置&#xff0c;Artifactory 提供了生成反向代理的功能&#x…

spring boot学习第十三篇:使用spring security控制权限

该文章同时也讲到了如何使用swagger。 1、pom.xml文件内容如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instanc…

vue - - - - - vue3使用draggable拖拽组件

vue3使用draggable拖拽组件 一、组件安装二、插件使用三、遇到的问题1. missing required prop&#xff1a; “itemKey” 一、组件安装 yarn add vuedraggablenext // or npm i -S vuedraggablenext二、插件使用 <template><draggableitem-key"id"class&q…

吴恩达机器学习笔记十三 多分类问题(multiclass) Softmax 神经网络的softmax输出 softmax改进

多分类问题指可能会有多于两个的输出标签&#xff0c;而不只是0或1的问题。 Softmax算法是逻辑回归的一种推广。 例如 y 有四种可能的取值时&#xff1a; 成本函数 例如有十种类别的输出&#xff0c;此时称这个神经网络有一个softmax输出层或上层是softmax层 softmax layer有…

android高级面试视频,从入门到精通

大佬带你走进Android开发的世界&#xff0c;掌握了这些知识点&#xff0c;学习Android也可以很轻松。 核心分析内容 对于怎么学习Android&#xff0c;主要解决的是3个问题&#xff1a;学什么、怎么学 & 怎么用。 具体如下&#xff1a; 下面&#xff0c;我将带着上述几个问…