网站救助计划
1.为阅读体验,本站无任何广告,也无任何盈利方法,站长一直在用爱发电,现濒临倒闭,希望有能力的同学能帮忙分担服务器成本
2.捐助10元及以上同学,可添加站长微信lurenzhang888,备注捐助,网站倒闭后可联系站长领取本站pdf内容
3.若网站能存活下来,后续将会持续更新内容
题目描述
请实现一个函数用来匹配包含 .
和 *
的正则表达式。模式中的字符 .
表示任意一个字符,而 *
表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 “aaa” 与模式 “a.a” 和 “abaca” 匹配,但与 “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 的小写字母以及字符 . 和 *,无连续的 ‘*’。
算法
(动态规划) 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
,将它们作为一个整体处理,
- 如果当前字符 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] == '.');
- 如果当前字符 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++ 代码
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];
}
};
Java 代码
public class Solution {
public boolean isMatch(String s, String p) {
int n = s.length(), m = p.length();
s = " " + s;
p = " " + p;
boolean[][] f = new boolean[n + 1][m + 1];
// 两个空串匹配
f[0][0] = true;
for (int i = 0; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) {
// 如果 p[j] 的下一个字符是 * 就不处理当前字符
if (j + 1 <= m && p.charAt(j + 1) == '*') {
continue;
}
// p[j + 1] 不是 *,那就看 f[i - 1][j - 1] 是否匹配且当前字符是否匹配
if (i > 0 && p.charAt(j) != '*') {
f[i][j] = f[i - 1][j - 1] && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.');
} else if (p.charAt(j) == '*') {
// 与 0 个第 j - 1 个字母匹配 || 【1 个 || 2 个 ... 进行了状态转移优化(完全背包优化)】
f[i][j] = f[i][j - 2] || (i > 0 && f[i - 1][j] && (s.charAt(i) == p.charAt(j - 1) || p.charAt(j - 1) == '.'));
}
}
}
return f[n][m];
}
}
Python 代码
class Solution:
def isMatch(self, s: str, p: str) -> bool:
n, m = len(s), len(p)
s = " " + s
p = " " + p
f = [[False] * (m + 1) for _ in range(n + 1)]
# 两个空串匹配
f[0][0] = True
for i in range(n + 1):
for j in range(1, m + 1):
# 如果 p[j] 的下一个字符是 * 就不处理当前字符
if j + 1 <= m and p[j + 1] == '*':
continue
# p[j + 1] 不是 *,那就看 f[i - 1][j - 1] 是否匹配且当前字符是否匹配
if i > 0 and p[j] != '*':
f[i][j] = f[i - 1][j - 1] and (s[i] == p[j] or p[j] == '.')
elif p[j] == '*':
# 与 0 个第 j - 1 个字母匹配 || 【1 个 || 2 个 ... 进行了状态转移优化(完全背包优化)】
f[i][j] = f[i][j - 2] or (i > 0 and f[i - 1][j] and (s[i] == p[j - 1] or p[j - 1] == '.'))
return f[n][m]
本文由读者提供,Github地址:https://github.com/tonngw
点击面试手册,获取本站面试手册PDF完整版