leetcode10 正则表达式匹配 题解

这题面给的真是一坨goushi啊,中英文题面全是错的的地方。

题目大意

给你两个字符串 s s s p p p 其中 p p p 是一个所谓的正则表达式的字符串。 s s s 由小写英文字母组成, p p p 由小写英文字母、‘.’、‘*’ 组成,然后问你 p p p 能不能匹配 s s s

“.”:可以匹配一个且仅一个字符
“X*”(这里的X表示一个小写英文字母或者’.'):可以匹配零个或者多个 X(可以是多个 ‘.’)(这里的描述和原本题目描述的不一样)

然后问你能不能匹配上,能的话返回一个 true,否则返回一个 false

为了防止大家不懂,这里给大家多来几个样例。

  1. s: a   a   a a\ a\ a a a a
    p: a   ∗ a\ * a 
    这里的 a   ∗ a\ * a  匹配了字符串 s 里面的三个 a,结果是 true
  2. s: a   a   a a\ a\ a a a a
    p: .   . .\ . . .
    这里的每一个 . 只能匹配一个 a,总共只能匹配两个,结果是 false
  3. s: a   b   c a\ b\ c a b c
    p: c ∗ a   .   c c*a\ .\ c ca . c
    前面的 c   ∗ c\ * c  匹配零个字符,然后 ‘ . . .’ 匹配一个字母 b,结果是 true
  4. s: a   b   c   d   e   f   g a\ b\ c\ d\ e\ f\ g a b c d e f g
    p: .   ∗ .\ * . 
    .   ∗ .\ * . ” 相当于七个 ‘ . . .’,结果是 true

题目原地址

大致思路

个人感觉 dp 特别好想,我们用 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 s 的前 i 个字母组成的子串,能不能匹配上 p 的前 j 个子串。是的话是 1,否是 0。

我们双重枚举 i 和 j,i 从 0 开始,j 从 1 开始,因为就如上面的第三个样例一样,p 的前缀(不了解的可以去搜一下)有可能相当于 s 的一个空串。

然后 s [ i − 1 ] s[i - 1] s[i1](因为字符串下标从 0 开始,所以要减一) 只有一种情况(小写字母)。
但是 p [ j − 1 ] p[j - 1] p[j1] 的情况就比较多了。

  • p [ j − 1 ] = = ′ . ′ p[j - 1] == '.' p[j1]==. p [ j − 1 ] p[j - 1] p[j1] 肯定能匹配 s [ i − 1 ] s[i - 1] s[i1],只要 d p [ i − 1 ] [ j − 1 ] dp[i - 1][j - 1] dp[i1][j1] 是 true, 那么 d p [ i ] [ j ] dp[i][j] dp[i][j] 也必定是 true。
  • p [ j − 1 ] p[j - 1] p[j1] 是一个字符:如果 p [ j − 1 ] = = s [ i − 1 ] p[j - 1] == s[i - 1] p[j1]==s[i1],那么只要 d p [ i − 1 ] [ j − 1 ] dp[i - 1][j - 1] dp[i1][j1] 是 true,那么 d p [ i ] [ j ] dp[i][j] dp[i][j] 肯定也是 true,但是如果 p [ j − 1 ] ≠ s [ i − 1 ] p[j - 1] \ne s[i - 1] p[j1]=s[i1] 那么 d p [ i ] [ j ] dp[i][j] dp[i][j] 肯定是 false。
  • p [ j − 1 ] = = ′ ∗ ′ p[j - 1] == '*' p[j1]==:这种情况比较麻烦,首先会有 ‘ ∗ * ’ 和 它前面的字母相当于空串的情况,即:只要 d p [ i ] [ j − 2 ] dp[i][j - 2] dp[i][j2] 是 true,那么 d p [ i ] [ j ] dp[i][j] dp[i][j] 肯定也是 true;如果 p [ j − 2 ] p[j - 2] p[j2] 是一个字母或者 ‘.’ 的话,那么就要分情况讨论了,大致分为以下五种情况:
  1. p [ j − 2 ] ≠ s [ i − 1 ] p[j - 2] \ne s[i - 1] p[j2]=s[i1] AND p [ j − 2 ] ≠ ′ . ′ p[j - 2] \ne '.' p[j2]=.:根本匹配不上, p [ j − 1 ] p[j - 1] p[j1] p [ j − 2 ] p[j - 2] p[j2] 只能充当空串,不用管;

  2. p [ j − 2 ] = s [ i − 1 ] p[j - 2] = s[i - 1] p[j2]=s[i1] AND p [ j − 1 ] p[j - 1] p[j1] p [ j − 2 ] p[j - 2] p[j2] 匹配不止一个字符:图1

因为我们我们枚举的顺序是从小到大,例如 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j] 的情况我们是知道的,那么此时只要 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j] 是 true,就可以确定当前的状态是这种情况,我们直接让 d p [ i ] [ j ] dp[i][j] dp[i][j] 等于 true;

  1. p [ j − 2 ] = s [ i − 1 ] p[j - 2] = s[i - 1] p[j2]=s[i1] AND p [ j − 1 ] p[j - 1] p[j1] p [ j − 2 ] p[j - 2] p[j2] 且只匹配一个字符:
    图2
    只要 d p [ i − 1 ] [ j − 2 ] dp[i - 1][j - 2] dp[i1][j2] 是 true,那么我们就可以判断当前状态是这种情况,让 d p [ i ] [ j ] dp[i][j] dp[i][j] 等于 true;

    1. 第四种和第五种情况就是 p [ j − 2 ] = ′ . ′ p[j - 2] = '.' p[j2]=. 但是 p [ j − 2 ] p[j - 2] p[j2] p [ j − 1 ] p[j - 1] p[j1] 匹配多个和只匹配一个字符,两种情况,不需要判断 s [ i − 1 ] s[i - 1] s[i1] p [ j − 2 ] p[j - 2] p[j2],剩下的和情况 2、3 类似。看看 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j] d p [ i − 1 ] [ j − 2 ] dp[i - 1][j - 2] dp[i1][j2] 是不是 true 就行了。

最后答案是 d p [ s . s i z e ( ) ] [ p . s i z e ( ) ] dp[s.size()][p.size()] dp[s.size()][p.size()],时间复杂度就是双重枚举的复杂度。

代码

我感觉 vector 好难用啊,OIer 表示不是很适应。

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(); 
        int m = p.size();

        auto isLetter = [&](char c){ // 判断是不是小写字母
            return (c >= 'a' && c <= 'z');
        };

        vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
        dp[0][0] = 1;
        for (int i = 0; i <= n; i++) // 记得这里是从 0 开始
            for (int j = 1; j <= m; j++)
            {
                if (p[j - 1] == '*') // 这里写的有点重复了,大家可以把他简化一下
                {
                    dp[i][j] |= dp[i][j - 2]; // '*' 和它前面的字母相当于空串的情况
                    if (i != 0 && j != 1 && p[j - 2] == s[i - 1])
                        dp[i][j] |= dp[i - 1][j], dp[i][j] |= dp[i - 1][j - 2]; // 情况 2 和 3
                    if (i != 0 && p[j - 2] == '.')
                        dp[i][j] |= dp[i - 1][j], dp[i][j] |= dp[i - 1][j - 2]; // 情况 4 和 5
                }
                else
                {
                    if (i == 0) // 注意不要越界
                        continue;
                    if (isLetter(p[j - 1]))
                    {
                        if (p[j - 1] != s[i - 1]) // 匹配不上直接 false
                            dp[i][j] = false;
                        else // 匹配上了就看看前面能不能匹配上
                            dp[i][j] |= dp[i - 1][j - 1];
                    }
                    else // 因为 p[j - 1] 是 '.' 无论 s[i - 1] 是啥都无所谓,直接看前面
                        dp[i][j] |= dp[i - 1][j - 1];
                }
            }
        return dp[n][m];
    }
};

结果

结果
我发现我每次写 hard 用时都比别人大。







作者能力有限,如果有任何错误之处,还请各位指教。(~ ̄▽ ̄)~

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值