剑指 Offer 19. 正则表达式匹配
题目
请实现一个函数用来匹配包含'. '
和'*'
的正则表达式。模式中的字符'.'
表示任意一个字符,而'*'
表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"
与模式"a.a"
和"ab*ac*a"
匹配,但与"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 = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
s
可能为空,且只包含从a-z
的小写字母。p
可能为空,且只包含从a-z
的小写字母以及字符.
和*
,无连续的'*'
。
思路
根据题意使用动态规划解决这个问题,分为以下三步走:
1.定义dp
我们可以定义一个二维dp数组,使得dp[i][j]
代表当s长度为i,p长度为j时的匹配状态
。
2.寻找关系
对于正则匹配式s[i] vs p[j]
配对的情况,我们可以分为以下情况讨论:
(1).p[j]
为普通字符,如a
,那么可以分为以下两种情况讨论:
1). s[i]=p[j]
,此时dp[i][j]
的值取决于前一个字符的匹配状态,即dp[i][j]=dp[i-1][j-1]
;
2). s[i]!=p[j]
,此时dp[i][j]=false
。
(2).p[j]='.'
,因为.
是万能符号,我们可以认为s[i]=p[j]
,同(1).1),此时dp[i][j]=dp[i-1][j-1]
。
这两种情况可以合并成一种情况,即
当p[j]='.'||p[j]==s[i]时,dp[i][j]=dp[i-1][j-1]; 否则 dp[i][j]=false
。
(3).p[j]='*'
,因为’*‘
可表示0个或者n个前面的字符,注意这里表示0个意思代表a*为空,a为'*'前面的某个字符
,因此可以分两种情况讨论:
1). s[i]!=p[j-1]
,这种情况’*‘
只能代表0个前面的字符,所以此时dp[i][j]
的值取决于p[j]
前两个字符与s[i]
的匹配状态,即dp[i][j]=dp[i][j-2]
;
2). s[i]=p[j-1]
,此时’*'
可以代表任意个前面的字符————0/1/2+个,这三种情况选一种符合要求即可。
’*‘
匹配0个时,dp[i][j]=dp[i][j-2]
;
’*‘
匹配1个时,dp[i][j]=dp[i][j-1]
;
’*‘
匹配2+个时,可任意选取一个抵消掉s[i]
位置的字符,所以dp[i][j]=dp[i-1][j]
;
所以,此时的dp[i][j]
可以表示为dp[i][j]=dp[i][j-2]||dp[i][j]=dp[i][j-1]||dp[i-1][j]
。
3.寻找初始值
dp数组的初始值从dp[0][0]
,我们需要找出i=0
时的所有初始值。
dp[0][j] = true, j = 0;
dp[0][j] = false, j = 1;
dp[0][j] = false, j >= 2 && p[j] != '*';
dp[0][j] = dp[0][j-2], j >= 2 && p[j] = '*';
代码
class Solution {
public:
bool isMatch(string s, string p) {
if(s.empty() && p.empty())
return true;
int n = s.length(), m = p.length();
vector<vector<bool>> dp(n+1, vector<bool>(m+1, false));
dp[0][0] = true;
for(int j = 1; j <= m; ++j){
if(p[j - 1] == '*')
dp[0][j] = dp[0][j - 2];
}
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
// 当p[j-1] != '*'
if(p[j - 1] != '*'){
if(p[j - 1] == '.' || p[j - 1] == s[i - 1])
dp[i][j] = dp[i-1][j-1];
}
else{ //p[j-1] == '*'
if(p[j - 2] != s[i-1] && p[j - 2] != '.')
dp[i][j] = dp[i][j-2];
else
dp[i][j] = dp[i][j-2] || dp[i][j-1] || dp[i-1][j];
}
}
}
return dp[n][m];
}
};