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

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类型来进行状态压缩。

posted @   Un-Defined  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示