首页 > 其他分享 >[leetcode] 10. 正则表达式匹配

[leetcode] 10. 正则表达式匹配

时间:2023-09-22 16:23:34浏览次数:55  
标签:10 cnt 匹配 字符 正则表达式 ++ int leetcode dp

10. 正则表达式匹配

给你一个字符串 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 的小写字母,以及字符 .*
  • 保证每次出现字符 * 时,前面都匹配到有效的字符

题解

久别重逢的leetcode题。

一开始的思路是用dfs暴力过,结果要么超时,要么爆内存。最后还是用dp过了。

#include<bits/stdc++.h>
using namespace std;

class Solution {
public:

    static const int N = 30;

    int dp[N][N];

    bool isMatch(string s, string p) {
        int lens = s.length(),lenp = p.length();
        char t[N][2] = {};
        int cnt = 0;
        for (int i = 0;i < lenp; i++){
            if (p[i + 1] == '*'){
                t[cnt][0] = p[i];
                t[cnt][1] = p[i + 1];
                cnt++;
                i++;
            }else{
                t[cnt++][0] = p[i];
            }
        }
        bool start = false;
        for (int i = 0; i < cnt; i++){
            for (int j = 0; j < lens; j++){
                if ((s[j] == t[i][0]) || (t[i][0] == '.')){
                    if (i == 0){
                        if (j == 0){
                            dp[i][j] = 1;
                        }
                    } else{
                        if (j == 0){
                            if (start == false){
                                dp[i][j] = 1;
                            }
                        } else{
                            dp[i][j] |= dp[i - 1][j - 1];
                        }
                    }
                }
            }
            if (t[i][1] == '*'){
                for (int j = 0; j < lens; j++){
                    if (i != 0){
                        dp[i][j] |= dp[i - 1][j];
                    }
                    if ((j != 0) && ((s[j] == t[i][0]) || (t[i][0] == '.'))){
                        dp[i][j] |= dp[i][j - 1];
                    }
                }
            } else{
                start = true;
            }
        }
        return dp[cnt-1][lens-1];
    }
};

int main(){
    string s,p;
    cin>>s>>p;
    cout<<Solution().isMatch(s,p);
}

s=abcd,p=a*b*c*.为例梳理一下思路。

对于p=a*b*c*.,将其拆分成a*b*c*. 四个匹配操作。这样{}* 表示匹配0到多个{}内的字符,{} 表示匹配1个{}内的字符。对于每个匹配操作,不论它后面是否带*,都是要尝试能不能匹配1个{}字符的。我们用数组 char t[4][2] 来记录每一个匹配操作。

dp的行,枚举字符串p中匹配操作的个数。

dp的列,枚举字符串s中的每一个字符。

dp[i][j] 表示第i个匹配之后,子字符串s.substr(0,j+1)是否匹配成功。

先处理只匹配1个字符的情况。

(s[j] == t[i][0]) || (t[i][0] == '.')成立,即匹配成功时

  • 可以转移状态dp[i][j] |= dp[i-1][j-1]。对应代码39行

  • 需要注意当 j=0 时,我们在匹配 s 中的第一个字符。如果 p 在匹配操作 i 之前,有一个必须匹配一个字符的操作,比如 s=bb,p=ab*,这样子虽然 t[1][0]==s[0],匹配成功,但是 p 在这之前有个 a 是必须匹配的,所以不能进行状态转移。需要进行特判。

    这里用start来判断之前是否有必须要匹配的字符,即在匹配操作 i 之前,是否有存在一个操作没有*。

    如果start==false,则直接将 dp[i][j] = 1 即可。

    该部分对应代码34-38行。

  • 当 i == 0,即在处理第0个匹配操作时,由于没有之前的状态可以进行转移,所以直接判断第一个字符是否匹配成功即可。对应代码29-33行

t[i][1]==*,即当前操作带*时

  • 处理匹配0个的情况。该情况下,直接将上一行的 dp状态 转移到下一行即可,dp[i][j] |= dp[i-1][j]。对应代码46-48行
  • 处理匹配大于1个的情况。当前行已经处理好了匹配1个情况,因此在当前结果的基础上,如果(j != 0) && ((s[j] == t[i][0]) || (t[i][0] == '.'))成立,则进行状态转移 dp[i][j] |= dp[i][j - 1];。对应挨代码49-51行

最终输出 dp[cnt-1][lens-1]的值即可。
这里dp每一行最多20个bool类型,而且进行的操作都是或操作,因此可以用int类型来进行状态压缩。

标签:10,cnt,匹配,字符,正则表达式,++,int,leetcode,dp
From: https://www.cnblogs.com/EIPsilly/p/17722685.html

相关文章

  • WIN10克隆到新SSD固态硬盘的崎岖经历
    Duetomylaptop’sdiskbeingonly250GB,whichIhaveusedformanyyears,DiskCissoontobefull.WhenIrunWindows10andotherprograms,itrunsveryslowly,affectingmyworkanddrivingmecrazy.Therefore,Idecidedtobuya1TBSSDdisk......
  • 常用正则表达式
    一、校验数字的表达式数字:[1]*$n位的数字:^\d{n}$至少n位的数字:^\d{n,}$m-n位的数字:^\d{m,n}$零和非零开头的数字:^(0|[1-9][0-9]*)$非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$带1-2位小数的正数或负数:^(-)?\d+(.\d{1,2})$正数、负数、和小数:^(-|+)?\d......
  • Typescript 测试驱动开发 TDD (10)
    测试设置和拆卸(Testsetupandteardown)在运行特定的测试之前,我们可能希望先执行一些代码。这可能是为了初始化一个特定的变量,或者确保对象的依赖关系已经设置好。同样地,我们可能希望在特定的测试运行后执行一些代码,甚至在整个测试套件运行完毕后执行。为了说明这一点,请考虑......
  • 【LeetCode】收集树中金币
    链接题目给你一个n个节点的无向无根树,节点编号从0到n-1。给你整数n和一个长度为n-1的二维整数数组edges,其中edges[i]=[ai,bi]表示树中节点ai和bi之间有一条边。再给你一个长度为n的数组coins,其中coins[i]可能为0也可能为1,1表示节点i处有......
  • Ubuntu 23.10/24.04 LTS 放弃默认使用 snap 版 CUPS 打印堆栈
    导读Canonical的开发者、OpenPrinting的项目负责人TillKamppeter今年5月表示,计划在Ubuntu23.10(ManticMinotaur)上默认使用Snap版本的CUPS打印堆栈。不过经过数月的测试,官方放弃了这项决定。Ubuntu23.10(ManticMinotaur)和Ubuntu24.04LTS发行版默认还是......
  • 依次输入10个数,要求输出最大值
    intmain(){ intn=1; inti=1; intmax=1; scanf("%d",&max);while(n<10)//将第一个数赋给max,后面只需在输入9个数,即可将数比完,得到最大数//循环9次 { scanf("%d",&i); if(max<=i)  max=i; n++; } printf("%d",max); re......
  • P1075 [NOIP2012 普及组] 质因数分解
    算法一根据唯一分解定理,小于\(n\)的最大的能整除\(n\)的整数一定就是答案,可以暴力枚举。时间复杂度\(O(n)\),实际得分\(60\)。算法二发现算法一不能通过的原因是较大的那个质数可能的取值范围太大了。而较小的那个质数一定小于等于\(\sqrtn\),我们枚举它即可。时间复......
  • 洛谷P5104 红包发红包
    我们假设他是离散的设[0,w]这个区间有i个数那么第一个人期望获得的钱数\(E(1)=\frac{1}{i}\sum_{j=1}^{i}\frac{w}{i}j=\frac{w(1+i)}{2i}\)因为这个区间实际上有无数个数,故令i趋于无穷,有\(E(1)=\frac{w}{2}\)那么轮到第二个人的时候还剩下\(w-\frac{w}{2}=\frac{w}{2}\)这么......
  • Cannot initiate the connection to cn.archive.ubuntu.com:80 (2403:2c80:5::6). - c
     版本:ubuntu22.04 Cannotinitiatetheconnectiontocn.archive.ubuntu.com:80(2403:2c80:5::6).-connect(101:Networkisunreachable) 嗯,被墙了。找到/etc/apt/source.list替换里面的源为清华源 ubuntu|镜像站使用帮助|清华大学开源软件镜像站|Tsinghu......
  • 10分钟设置免费海外远程桌面
    前言本教程将向您介绍如何使用AmazonLightsail服务的免费套餐轻松搭建属于您的远程桌面。依托于Amazon全球可用区,您可以在世界各地搭建符合您配置需求的远程桌面。本教程需要先拥有亚马逊云科技海外账户。现在注册亚马逊云科技账户可以享受12个月免费套餐,包括EC2等多种热......