给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符
‘*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
思路:
因为有*号可以匹配多个字符,使用动态规划来保存所有匹配情况。
dp[i][j]表示s的前i个字符和p的前j个字符匹配。
有了状态,接下来就是考虑状态转移方程。
1.如果是字符或是’.’,就很简单。
dp[i][j] = dp[i-1][j-1] (s[i]==p[j] || p[j]=='.')
2.如果p[j]是’*’,由于 可以匹配多个字符。这里考虑:
(1)不匹配。 判断去掉*和前一个字符是否匹配
dp[i][j] = dp[i][j-2])
(2) 先匹配s[i]字符,再检查i之前的字符是否匹配, 如果不匹配则dp[i][j]不匹配。()
dp[i][j] = dp[i-1][j] (s[i]==p[j] || p[j]=='.')
bool isMatch(string s, string p) {
//dp[i][j] 表示s的前i个字符和p的前j个字符匹配
int n = s.size(), m = p.size();
if(m == 0){ // 边界特判
if(n == 0) return true;
else return false;
}
if(p[0] == '*') return false; // 星号前面没有其他字符是非法的状态
vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
dp[0][0] = true;
s = "#" + s; //加一个字符是为了索引都转为1开始
p = "#" + p;
for(int i = 2; i <= m; i ++){ // 处理0的情况。也就p和空字符串匹配的情况
if(p[i] == '*') dp[0][i] = dp[0][i - 2];
// else dp[0][i] = false;
}
for(int i = 1; i <= n; i ++)
{ // 开始进行状态转移
for(int j = 1; j <= m; j ++)
{
if(p[j] != '*')
{
// 不是星号,这样就只需判断s[i] 和 p[j] ,字符是否能匹配
dp[i][j] = dp[i-1][j-1] && (s[i] == p[j] || p[j] == '.');
}
else
{
//*号,这里只有两种情况,一个就是匹配0个,还有匹配了s[i]。
//比如 aaab和aaab* b*匹配一个b, 然后判断aaa 和 aaab*是否匹配
dp[i][j] = dp[i][j - 2] || dp[i-1][j] && (s[i] == p[j - 1] || p[j - 1] == '.');
}
}
}
return dp[n][m];
}