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

news2025/4/8 16:59:58

在这里插入图片描述

算法沉淀——动态规划之两个数组的 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.coloradmin.cn/o/1478580.html

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

相关文章

音频提取使用什么方法?视频提取音频

在数字技术与多媒体日益普及的今天&#xff0c;音频提取已成为一个常见且重要的任务。无论是为了制作视频、编辑音乐&#xff0c;还是进行语音识别和分析&#xff0c;我们都需要从原始材料中提取音频。那么&#xff0c;音频提取通常使用什么方法呢&#xff1f; 1. 使用专业的音…

Cap0:TensorRT环境搭建

文章目录 1、安装TensorRT1.1、下载TensorRT压缩包1.2、配置环境变量 2、测试2.1、测试源码2.2、编译源码 1、安装TensorRT TensorRT是针对NVIDIA显卡设备的加速方案&#xff0c;你要使用TensorRT则证明你有一定的深度学习基础&#xff0c;那么在你的Ubuntu上配置好显卡驱动、…

【Unity每日一记】角色控制器Character Contorller

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

Django学习记录——管理员-登录注销的实现

1.管理员案例 1.1管理员数据库 1.1.1 表结构 1.1.2 管理员表的建立 class Admin(models.Model):"""管理员表"""username models.CharField(max_length32, verbose_name"用户名")password models.CharField(max_length64, verbose…

前端AR图像增强 + 图像追踪 + 模型渲染

文章目录 背景介绍技术介绍准备目标图片准备3D模型整合到一起演示代码地址背景介绍 本文实现web端html实现AR识别功能 在日常生活中常常看到AR虚拟现实相结合的案例 如下图的效果匹配到目标图片后展示3D模型 从而提高真实度 AR识别 技术介绍 想要达到效果有以下几步是必须的 准…

https://htmlunit.sourceforge.io/

https://htmlunit.sourceforge.io/ 爬虫 HtmlUnit – Welcome to HtmlUnit HtmlUnit 3.11.0 API https://mvnrepository.com/artifact/net.sourceforge.htmlunit/htmlunit/2.70.0 https://s01.oss.sonatype.org/service/local/repositories/releases/content/org/htmlunit…

西软云XMS operate XXE漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

【Java程序员面试专栏 数据结构】一 高频面试算法题:数组

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,本篇主要聊聊数组,包括数组合并,滑动窗口解决最长无重复子数组问题,图形法解下一个排列问题,以及一些常见的二维矩阵问题,所以放到一篇Blog中集中练习 题目…

ChatGpt 使用fetch-event-source实现sse流式处理

microsoft/fetch-event-source 是一个由微软提供的库&#xff0c;用于在客户端和服务器之间建立基于 EventSource 的连接。EventSource 是一种 HTTP 协议&#xff0c;允许服务器向客户端推送实时事件流。该库提供了对 EventSource 协议的封装&#xff0c;使得在前端 JavaScript…

CCF-A类 IEEE VIS‘24 3月31日截稿!探索可视化技术的无限可能!

会议之眼 快讯 IEEE VIS (IEEE Visualization Conference )即可视化大会将于 2024 年 10月13日 -18日在美国佛罗里达州皮特海滩的信风岛大海滩度假举行&#xff01;圣彼得海滩&#xff0c;以其迷人的日落和和煦的微风&#xff0c;作为激发创造力和促进可视化社区内合作的完美背…

数据结构与算法之美学习笔记:55 | 算法实战(四):剖析微服务接口鉴权限流背后的数据结构和算法

目录 前言鉴权背景介绍如何实现快速鉴权&#xff1f;限流背景介绍如何实现精准限流&#xff1f;总结引申 前言 本节课程思维导图&#xff1a; 微服务是最近几年才兴起的概念。简单点讲&#xff0c;就是把复杂的大应用&#xff0c;解耦拆分成几个小的应用。这样做的好处有很多。…

外汇天眼:Sumsub推出播客,讨论最新的欺诈威胁

Sumsub&#xff0c;一家全球验证平台&#xff0c;今天宣布推出自己的播客&#xff0c;名为《什么是欺诈&#xff1f;》。节目将邀请来自各行各业的专业嘉宾&#xff0c;包括人工智能、网络安全、金融科技、加密货币和互联网游戏等领域的专家。对话将集中讨论数字欺诈如何影响企…

服了,阿里云服务器价格和腾讯云1元之差,如何选择?

2024年阿里云服务器和腾讯云服务器价格战已经打响&#xff0c;阿里云服务器优惠61元一年起&#xff0c;腾讯云服务器62元一年&#xff0c;2核2G3M、2核4G、4核8G、8核16G、16核32G、16核64G等配置价格对比&#xff0c;阿腾云atengyun.com整理阿里云和腾讯云服务器详细配置价格表…

Apache Paimon Append Queue表解析

a) 定义 在此模式下&#xff0c;将append table视为由bucket分隔的queue。 同一bucket中的每条record都是严格排序的&#xff0c;流式读取将完全按照写入顺序将record传输到下游。 使用此模式&#xff0c;无需特殊配置&#xff0c;所有数据都将作为queue进入一个bucket&…

XSS中级漏洞(靶场)

目录 一、环境 二、正式开始闯关 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0B 0x0C 0x0D 0x0E ​ 0x0F 0x10 0x11 0x12 一、环境 在线环境&#xff08;gethub上面的&#xff09; alert(1) 二、正式开始闯关 0x01 源码&#xff1a; 思路&#xff1a;闭…

【vue3学习笔记】Suspense组件;vue3中的其它改变

尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 课程 P167节 《Suspense组件》笔记 想要学习suspense&#xff0c;先来了解静态组件与异步组件。 静态引入与异步引入&#xff1a; 在network中将网速调慢&#xff0c;观察在静态引入和异步引入模式下&#xff0c;两个组件的加载…

Programming Abstractions in C阅读笔记:p306-p307

《Programming Abstractions in C》学习第75天&#xff0c;p306-p307总结&#xff0c;总计2页。 一、技术总结 1.Quicksort algorithm(快速排序) 由法国计算机科学家C.A.R(Charles Antony Richard) Hoare&#xff08;东尼.霍尔&#xff09;在1959年开发(develop), 1961年发表…

力扣每日一题 使二叉树所有路径值相等的最小代价 满二叉树 贪心

Problem: 2673. 使二叉树所有路径值相等的最小代价 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public int minIncrements(int …

瑞_23种设计模式_外观模式

文章目录 1 外观模式&#xff08;Facade Pattern&#xff09;1.1 介绍1.2 概述1.3 外观模式的结构 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 jdk源码解析 &#x1f64a; 前言&#xff1a;本文章为瑞_系列专栏之《23种设计模式》的外观模式篇。本文中的部分…

一篇关于,搬运机器人的介绍

搬运机器人是一种能够自动运输和搬运物品的机器人。它们通常配备有传感器和导航系统&#xff0c;可以在工厂、仓库、医院或其他场所自主移动&#xff0c;并且可以根据预先设定的路径或指令进行操作。 搬运机器人可以用于搬运重物、物料搬运、装卸货物、仓库管理等任务。它们可以…