剑指 Offer 19. 正则表达式匹配

题目描述

请实现一个函数用来匹配包含$’. ‘$和$’‘$的正则表达式。模式中的字符$’.’$表示任意一个字符,而$’‘$表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串$”aaa”$与模式$”a.a”$和$”abaca”$匹配,但与$”aa.a”$和$”ab*a”$均不匹配。

示例 1:

1
2
3
4
5
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

1
2
3
4
5
输入:
s = "aa"
p = "a*"
输出: true
解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

示例 3:

1
2
3
4
5
输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

1
2
3
4
5
输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

示例 5:

1
2
3
4
输入:
s = "mississippi"
p = "mis*is*p*."
输出: false
  • $s$ 可能为空,且只包含从 $a-z$ 的小写字母。
  • $p$ 可能为空,且只包含从 $a-z$ 的小写字母以及字符 $.$ 和 $$,无连续的 $’‘$。

注意:本题与主站 10 题相同:https://leetcode-cn.com/problems/regular-expression-matching/


算法

(动态规划) $O(n^2)$

状态表示:$s$ 的前 $i$ 个字母 s[1~i] 和 $p$ 的前 $j$ 个字母是否匹配(下标从 $1$ 开始比较方便 )
状态计算:根据 p[j + 1] 是否为 * 分情况讨论,因为如果 p[j +1] = * 时那么 p[j-j+1] 是一个整体。($*$ 表示前面的一个字符可以出现任意多次)

特判:如果 $p[j + 1]$ 是 $*$ 的话那么不处理当前字符 $p[j]$,continue,将它们作为一个整体处理,

  1. 如果当前字符 $p[j]$ 不是 *,则 f[i][j] = true 当且仅当 s[1~i-1]p[1~j-1] 匹配且 s[i] 和 p[j] 匹配(或者 p[j] == '.' 也是匹配的)f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
  2. 如果当前字符 $p[j]$ 是 *,则遍历 $*$ 号前面的字符出现从 $0$ 次到任意多次与 $s[i]$ 进行匹配。f[i][j] = f[i][j - 2] || (i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));,这里经过了状态转移优化,这个优化类似于完全背包优化,都是用 f[i - 1][j] 替换了除出现 $0$ 次以外的循环遍历。

时间复杂度

$O(n^2)$

空间复杂度

$O(n^2)$

C++ 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
bool isMatch(string s, string p) {
int n = s.size(), m = p.size();
s = ' ' + s, p = ' ' + p;
vector<vector<bool>> f(n + 1, vector<bool>(m + 1));

// 两个空串匹配
f[0][0] = true;
// i 从 0 开始,因为 s[i] 是空串的时候可能会和 p[j] 匹配成功(当 j + 1 是 *,可以表示 p[j] 出现 0 次,此时两个串匹配成功)
for (int i = 0; i <= n; i ++ ) {
// j 从 1 开始,i = 0、j = 0 已经初始化过了,而对于 i != 0、j = 0,空串都不会匹配成功
for (int j = 1; j <= m; j ++ ) {
// 如果 p[j] 的下一个字符是 * 就不处理当前字符,因为 p[j] 和 * 在一块才能整体才能表示一个含义
if (j + 1 <= m && p[j + 1] == '*') continue;
// p[j + 1] 不是 *,那就看 f[i - 1][j - 1] 是否匹配且当前字符是否匹配
if (i && p[j] != '*') {
f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
} else if (p[j] == '*') {
// 与 0 个第 j - 1 个字母匹配 || 【1 个 || 2 个 ... 进行了状态转移优化(完全背包优化)】
f[i][j] = f[i][j - 2] || (i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
}
}
}

return f[n][m];
}
};
Author: tonngw
Link: https://tonngw.com/2022/07/09/剑指 Offer/剑指 Offer 19. 正则表达式匹配/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.