LeetCode第 10 题:正则表达式匹配 (C++)

10. 正则表达式匹配 - 力扣(LeetCode)
在这里插入图片描述

剑指 Offer 19. 正则表达式匹配 - 力扣(LeetCode)一样的题目

困难题,第一个难点就是读懂题目。。。

示例3我卡了好久啊,明明一眼就该看出来的东西,哭。。。

输入:
s = “ab”
p = “.*”
输出: true

解释: .* 表示可匹配零个或多个*任意字符.,这句话怎么读都读不通顺,理解为*匹配成前面的.,于是就变成..,就想开了。

递归

思路是不停地对头部元素进行匹配,如果头部元素相同,去掉头部再匹配剩余的字符串…

但是,如果头部元素不同,就返回false吗?并不是,看这个例子:

s: ab
p: b*ab

首元素s[0]、p[0] 不同,但实际上能匹配,因为 *(p[1]) 的特殊功能,可以将 b 变为0个(也就是为空)。*的特殊功能仅仅对它的前面那个字符生效,所以需要往p的首元素后面多考虑一个元素,也就是p[1]。

1、如果p的第二个元素不为 * ,那么仅仅考虑首元素即可:

  • s[0] == p[0],递归判断isMatch(s.substr(1), p.substr(1));
  • s[0] != p[0],返回false

2、如果p的第二个元素p[1]* 时,需要考虑几种情况:

  • s[0] != p[0],此时要想匹配能够进行下去,必须使用*消去p[0],也就是说接着判断isMatch(s, p.substr(2));即可,示例4的第一次消除情况就是这个。
  • s[0] == p[0]
    a、可以选择使用*消去p[0],接着判断isMatch(s, p.substr(2));
    b、不进行消去的话,接着判断isMatch(s.substr(1), p);
    以上两种选择任一种能够匹配成功即可。

比较难理解的是2.b这一条,为什么是判断isMatch(s.substr(1), p);呢?来看看示例4:

s = "aab" p = "c*a*b"

第一步:首元素s[0] != p[0],但是p[1] == *,要想匹配进行下去,必须使用 * 消除p[0],也就是判断isMatch(s, p.substr(2));

那么

s = "aab" p = "a*b"

第二步:首元素s[0] == p[0],有两个分支:
a、使用*消去p[0]:剩下aabb,最后匹配失败
b、由于*可以匹配零个或多个前面的那一个元素,肉眼观察的话此处我们使用*匹配1个前面的字符,就可以匹配成功,但是怎么将这种匹配多个前面的字符的逻辑用代码表示呢?遍历到当前点我们并不知道需要匹配多少个前面的那一个元素,所以换一种方式来表达:我们通过去掉s的首元素来实现这种选择。也就是说接下来:

s = "ab" p = "a*b"

再往下还可以去掉s的首元素:

s = "b" p = "a*b"

这样就回到了第一步,就可以走下去了。

当然前面判断首元素匹配的时候记得考虑 .

class Solution {
public:
    bool isMatch(string s, string p) {
        if(p.empty())   return s.empty();
        bool headMatch = !s.empty() && (p[0] == s[0] || p[0] == '.');
        if(headMatch){//首元素匹配
            if(p.size() > 1 && p[1] == '*'){//可以消除
                if(isMatch(s, p.substr(2)))    return true;//消除p[0]
                if(isMatch(s.substr(1), p))    return true;
                return false;
            }else//无法消除
                return isMatch(s.substr(1), p.substr(1));//去掉首元素
        }else{//首元素不匹配
            if(p.size() > 1 && p[1] == '*') return isMatch(s, p.substr(2));//进行消除
            return false;//无法匹配
        }
    }
};

看了别人的代码,可以修改一下,不过不容易理解:

class Solution {
public:
    bool isMatch(string s, string p) {
        //不停地减去s,p相同的头部,直到某一个或者全部被剪空,就有结论了
        if(p.empty())   return s.empty();
        // 先检查首元素
        bool head_match = !s.empty() && (s[0] == p[0] || p[0] == '.');
        // 如果下一个元素是‘*’
        if(p.size() >= 2 && p[1] == '*')
            return isMatch(s, p.substr(2)) || (head_match && isMatch(s.substr(1), p));
        else
            // 一般情况,首元素匹配上的条件下均去掉首元素再匹配
            return head_match && isMatch(s.substr(1), p.substr(1));
    }
};

动态规划

状态推导:正则表达式匹配 - 正则表达式匹配 - 力扣(LeetCode)

class Solution {
public:
    bool dp[35][35];//dp[i][j]表示s的前i个字符与p中的前j个字符是否能够匹配
    bool isMatch(string s, string p) {
        int m = s.size(), n = p.size();
        dp[0][0] = true;//两个空串可匹配
        for(int i = 2; i <= n; ++i){//初始化,s为空的情况
            if(p[i-1] == '*')   dp[0][i] = dp[0][i-2];
        }
        for(int i = 1; i <= m; ++i){
            for(int j = 1; j <= n; ++j){          
                if(p[j-1] == '*'){
                    dp[i][j] |= dp[i][j-2];//消去
                    if(s[i-1] == p[j-2] || p[j-2] == '.')    dp[i][j] |= dp[i-1][j];
                }
                else if(s[i-1] == p[j-1] || p[j-1] == '.')    dp[i][j] |= dp[i-1][j-1];
            }
        }
        return dp[m][n];
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值