剑指 Offer 19. 正则表达式匹配

题目描述
请实现一个函数用来匹配包含’. ‘和’ * ‘的正则表达式。模式中的字符’.’ 表示任意一个字符,而’ * '表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。

示例 1:
输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
示例 2:
输入:
s = “aa”
p = “a*”
输出: true
解释: 因为 ‘’ 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 ‘a’。因此,字符串 “aa” 可被视为 ‘a’ 重复了一次。
示例 3:
输入:
s = “ab”
p = ".
"
输出: true
解释: "." 表示可匹配零个或多个(’’)任意字符(’.’)。
示例 4:
输入:
s = “aab”
p = “cab”
输出: true
解释: 因为 ‘’ 表示零个或多个,这里 ‘c’ 为 0 个, ‘a’ 被重复一次。因此可以匹配字符串 “aab”。
示例 5:
输入:
s = “mississippi”
p = "mis
isp."
输出: false
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母以及字符 . 和 ,无连续的 '’。

链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof

题解:
动态规划,本题与通配符匹配有差距,通配符匹配是*可以匹配任意多个字符包括零个字符,?匹配任意单个字符,而且与模式串中前一个字符没有关系,也就 * 与前一个字符没关系。本题目是 * 指的是前一个字符可以重复多次也包括0次。

p[ j ] 有两种情况:

  • p[j]==’ * ’
    字母 + 星号的组合在匹配的过程中,本质上只会有两种情况:
    1)匹配 s 末尾的一个字符,将该字符扔掉,而该组合还可以继续进行匹配;
    2)不匹配字符,将该组合扔掉,不再进行匹配。
    如果s[i] ! = p[j-1] 且 p[j-1] 1=‘.’ 那么当下的匹配可以看做是 字母+ 星号没有起作用,那么f[ i ][ j ] =f[ i ][ j-2 ]。
    如果s[i] == p[j-1] 要考虑匹配0次到多次 。 例如:“aaa” “aaaa*” 这样还要考虑匹配0次的情况。
    f[i][j] = f[i][j-2] || f[i-1][j-2] || f[i-2][j-2] ……|| f[i-k][j-2] (匹配0次||匹配1次)
    f[i-1][j] = f[i-1][j-2] || f[i-2][j-2]…
    二者合并:f[i][j] = f[i][j-2] || f[i-1][j]
  • p[j] 是小写字母
    f[ i ][ j ] =f[ i-1 ][ j-1 ] 其中s[ i ] = p[ j ] 或者 p[ j ] =’ . ’
class Solution {
public:
    bool isMatch(string s, string p) {
        s = " " + s;
        p = " " + p;
        int n = s.size(),m = p.size();
        vector<vector<bool>> dp(n+1,vector<bool>(m+1));
        dp[0][0]=true;
        for (int i = 1; i < n + 1; i++) 
        {
            for (int j = 1; j < m + 1; j++) 
            {
                // 不带 '*' 号时的匹配
                if (s[i - 1] == p[j - 1] || p[j - 1] == '.') 
                {
                    dp[i][j] = dp[i - 1][j - 1];
                } 
                else if (p[j - 1] == '*') 
                //暗示了j>=2,真正的字符串j从下标1开始,因此如果j==1,
                //那么p[j-1]=" ",则j应该从2开始才进入了真正的字符串
                {
                    // 考虑 '*' 时的两种情况
                    if (s[i - 1] != p[j - 2] && p[j - 2] != '.') 
                    {
                        dp[i][j] = dp[i][j - 2];
                    } 
                    else 
                    {
                        dp[i][j] = dp[i][j - 2] || dp[i - 1][j];
                    }
                }
            }
        }

        return dp[n][m];
    }
};

按照推导的公式的写法,需要在每个字符串前面补充了两个空格,如果只补充一个,对于 p[j]==’*’ 会丢到前面 j =1 以及 j = 0这两种情况,如果在条件中加上j-2>=0的条件的话。

class Solution {
public:
    bool isMatch(string s, string p) {
        s="  "+s;//增加了两个空格
        p="  "+p;
        int n = s.size(),m = p.size();
        vector<vector<int>> f(n+1,vector<int>(m+1));
        f[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(p[j]=='.' ||s[i]==p[j])
                {
                    f[i][j]=f[i-1][j-1];
                }
                else if(p[j]=='*')
                {
                    if(p[j-1]!='.'&&s[i]!=p[j-1])
                        f[i][j]=f[i][j-2] ;//匹配0次
                    else
                        f[i][j]=f[i][j-2]|| f[i-1][j];//匹配0次或者多次
                }
            }
        }
        return f[n][m];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值