【Leetcode】10. 正则表达式匹配 【字符串、动态规划】

给定一个字符串 ( s s s) 和一个字符模式 ( p p p)。实现支持 ‘ . . .’ 和 ‘ ∗ * ’ 的正则表达式匹配。

. . .’ 匹配任意单个字符。
∗ * ’ 匹配零个或多个前面的元素。
匹配应该覆盖整个字符串 ( s s s) ,而不是部分字符串。

说明:

s s s 可能为空,且只包含从 a − z a-z az 的小写字母。
p p p 可能为空,且只包含从 a − z a-z az 的小写字母,以及字符 . . . ∗ *
示例 1 1 1:

输入:s = “ a a aa aa
p = “ a a a
输出: f a l s e false false
解释: “ a a a” 无法匹配 “ a a aa aa” 整个字符串。
示例 2 2 2:

输入:
s s s = “ a a aa aa
p p p = “ a ∗ a* a
输出: t r u e true true
解释: ‘ ∗ * ’ 代表可匹配零个或多个前面的元素, 即可以匹配 ‘ a a a’ 。因此, 重复 ‘ a a a’ 一次, 字符串可变为 “ a a aa aa”。
示例 3 3 3:

输入:
s s s = “ a b ab ab
p p p = “ . ∗ .* .
输出: t r u e true true
解释: “ . ∗ .* .” 表示可匹配零个或多个(’ ∗ * ’)任意字符(’ . . .’)。
示例 4 4 4:

输入:
s s s = “ a a b aab aab
p p p = “ c ∗ a ∗ b c*a*b cab
输出: t r u e true true
解释: ‘ c c c’ 可以不被重复, ‘ a a a’ 可以被重复一次。因此可以匹配字符串 “ a a b aab aab”。
示例 5 5 5:

输入:
s s s = “ m i s s i s s i p p i mississippi mississippi
p p p = “ m i s ∗ i s ∗ p ∗ . mis*is*p*. misisp.
输出: f a l s e false false

思路:

1)递归。

  • 如果 p p p的下一个字符为 ∗ * ,那么 ( 1 ) (1) (1) s s s不为空且 p [ 0 ] = s [ 0 ] p[0] = s[0] p[0]=s[0] p [ 0 ] = ′ . ′ p[0] = '.' p[0]=.,即该位能够匹配时,那么这个时候由于 ∗ * 可以匹配多个前面字符,则递归描述为 ( s + 1 , p ) (s + 1, p) (s+1,p),在这里采取截取字符串的方式, ( s . s u b s t r ( 1 ) , p ) (s.substr(1), p) (s.substr(1),p) ( 2 ) (2) (2)当不满足 ( 1 ) (1) (1)条件时,则递归描述为 ( s , p . s u b s t r ( 2 ) ) (s, p.substr(2)) (s,p.substr(2))
  • 如果 p p p的下一个字符不为 ∗ * ,那么只有两个字符匹配,即 s s s不为空且 p [ 0 ] = s [ 0 ] p[0] = s[0] p[0]=s[0] p [ 0 ] = ′ . ′ p[0] = '.' p[0]=.成立时,才能继续匹配下一个字符,递归描述为 ( s . s u b s t r ( 1 ) , p . s u b s t r ( 1 ) ) (s.substr(1), p.substr(1)) (s.substr(1),p.substr(1))
class Solution {
public:
    bool isMatch(string s, string p) {
        if (p.empty()) return s.empty();
        if (p.size() > 1 && p[1] == '*') {
        	//两种匹配方式
            return isMatch(s, p.substr(2)) || 
            (!s.empty() && (s[0] == p[0] || p[0] == '.') 
            && isMatch(s.substr(1), p));
        } else {
        	//满足前面条件才能继续匹配
            return !s.empty() && (s[0] == p[0] || 
            p[0] == '.') && isMatch(s.substr(1), p.substr(1));
        }
    }
};

2)动态规划。实则对上述递归表达的解析。

  • d p [ i ] [ j ] = t r u e dp[i][j] = true dp[i][j]=true描述为 s s s中前 i i i个字符与 p p p中前 j j j个字符匹配
  • 如果 p [ j − 1 ] = ′ ∗ ′ p[j - 1] = '*' p[j1]=,那么对应上述的第 1 ) 1) 1)种情况,则有 d p [ i ] [ j ] = d p [ i ] [ j − 2 ] dp[i][j] = dp[i][j - 2] dp[i][j]=dp[i][j2] || ( i > 0 i > 0 i>0 && ( s [ i − 1 ] = = p [ j − 2 ] ∣ ∣ p [ j − 2 ] = = ′ . ′ s[i - 1] == p[j - 2] || p[j - 2] == '.' s[i1]==p[j2]p[j2]==. ) && d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j],即将上述的分析逆转一下
  • 如果p[j - 1] != ‘*’,那么对应上述的第2)种情况,则有$dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.
class Solution {
public:
    bool isMatch(string s, string p) {
        int m = s.size(), n = p.size();
        bool dp[m + 1][n + 1];
        memset(dp, false, sizeof(dp));
        dp[0][0] = true;
        for (int i = 0; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (j > 1 && p[j - 1] == '*') {
                    dp[i][j] = dp[i][j - 2] || (i > 0 && (s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1][j]);
                } else {
                    dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.');
                }
            }
        }
        return dp[m][n];
    }
};

你可能感兴趣的:(【Leetcode】)