剑指 Offer 19. 正则表达式匹配
题目
请实现一个函数用来匹配包含'. '和''的正则表达式。模式中的字符'.'表示任意一个字符,而''表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
思路
这里为了好理解假设 s 和 p 的索引都是从1开始的
注意:s的长度一定要从0开始枚举,因为p有可能会匹配空字符串
- 状态:设
f[i][j]
表示 s 的前 i 个字符 和 p 的前 j 个字符是否匹配 - 转移
p[j]
为普通字符或.
f[i][j] = f[i-1][j-1] (s[i] == p[j] || p[j] == '.')
f[i][j] = false (s[i] != p[j])
p[j]
为*
- 星号前字符重复0次:
f[i][j] = f[i][j-2]
- 星号前字符重复1次:
f[i][j] = f[i-1][j-2], (s[i] == p[j-1])
- 星号前字符重复2次:
f[i][j] = f[i-3][j-2], (s[i]==s[i-1]==p[j-1])
- ...
- 星号前字符重复0次:
- 优化:对于
p[j]
为*
的情况,我们要枚举这个组合到底匹配了s
中的几个字符f[i][j] = f[i-1][j] (s[i] == p[j-1])
f[i][j] = f[i][j-2] (s[i] != p[j-1])
代码
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];
};
int f[m+1][n+1];
memset(f, 0, sizeof(f));
f[0][0] = true;
for (int i = 0; i <= m; ++i) { // 外层循环一定要是原始字符串(好像也不一定?)
for (int j = 1; j <= n; ++j) {
if (p[j-1] == '*') {
f[i][j] |= f[i][j-2]; // 相当于重复 0 次
if (matches(i, j-1)) { // 重复 n 次
f[i][j] |= f[i-1][j];
}
}
else {
if (matches(i, j)) {
f[i][j] |= f[i-1][j-1];
}
}
}
}
return f[m][n];
}
};