给定一个字符串 ( s s s) 和一个字符模式 ( p p p)。实现支持 ‘ . . .’ 和 ‘ ∗ * ∗’ 的正则表达式匹配。
‘
.
.
.’ 匹配任意单个字符。
‘
∗
*
∗’ 匹配零个或多个前面的元素。
匹配应该覆盖整个字符串 (
s
s
s) ,而不是部分字符串。
说明:
s
s
s 可能为空,且只包含从
a
−
z
a-z
a−z 的小写字母。
p
p
p 可能为空,且只包含从
a
−
z
a-z
a−z 的小写字母,以及字符
.
.
. 和
∗
*
∗。
示例
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
c∗a∗b”
输出:
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*.
mis∗is∗p∗.”
输出:
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[j−1]=′∗′,那么对应上述的第 1 ) 1) 1)种情况,则有 d p [ i ] [ j ] = d p [ i ] [ j − 2 ] dp[i][j] = dp[i][j - 2] dp[i][j]=dp[i][j−2] || ( 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[i−1]==p[j−2]∣∣p[j−2]==′.′ ) && d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i−1][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];
}
};