LeetCode/正则表达式匹配
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配
- '.' 匹配任意单个字符
- '*' 匹配零个或多个前面的那一个元素
1. 暴力递归
由于*匹配零个或多个前面元素,所以遍历是有方向的,从后往前递归判断,使问题规模缩小
class Solution {
public:
bool isMatch(string s, string p) {
//只要有一种匹配成功就返回真
int m = s.size();int n = p.size();
return Match(s,p,m-1,n-1);//从后往前
}
bool Match(string &s,string &p,int i,int j){
//边界条件
if(i<0&&j<0) return true;//已经匹配完
else if(j<0) return false;//模板串遍历完,匹配失败
else if(i<0){//模板串有*可以自己消除
if(p[j]=='*') return Match(s,p,i,j-2);
return false;
}
if(s[i]==p[j]||p[j]=='.') return Match(s,p,i-1,j-1);//匹配一次
if(p[j]=='*'){
if(j==0) return false;//前面没数了
if(s[i]==p[j-1]||p[j-1]=='.')//跟前面的数匹配的话
{
//匹配零次前面字符
bool flag1 = Match(s,p,i,j-2);
//匹配一次
bool flag2 = Match(s,p,i-1,j-1);
//匹配多次
bool flag3 = Match(s,p,i-1,j);
return flag1|flag2|flag3;
}
else return Match(s,p,i,j-2);//不匹配的话跳过
}
return false;
}
};
2. 动态规划
显然方法一中有很多重复递归,用而二维数组将其记录,减少重复递归
这里直接从前往后动态规划
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
auto matches = [&](int i, int j) {//匿名函数
if (i == 0)//匹配失败
return false;
if (p[j - 1] == '.')
return true;//可以匹配成功
return s[i - 1] == p[j - 1];//相等匹配成功,不等失败
};
vector<vector<int>> f(m + 1, vector<int>(n + 1));//多出来的行列存储边界,默认为0
f[0][0] = true;//边界条件
for (int i = 0; i <= m; ++i) {//从左往右
for (int j = 1; j <= n; ++j) {//从上往下,从1开始,因为第一列为边界0
if (p[j - 1] == '*') {//第j个字符为*,这里其实默认了模板串第一个不会为*
f[i][j] |= f[i][j - 2];//选择跳过,匹配零次
if (matches(i, j - 1)) //不跳过的情况,前一个字符可以进行匹配
f[i][j] |= f[i - 1][j];//匹配多次
}
else if (matches(i, j)) //当前可匹配
f[i][j] |= f[i - 1][j - 1];//匹配一次
}
}
return f[m][n];//返回主串前m个字符和模板串前n个字符匹配情况
}
};