LeetCode 10. 正则表达式匹配
这道题是正则表达式的匹配,初始化和状态转移都比较繁琐,需要非常小心,测试样例也给的比较全一些。先做一道简化版的 LeetCode 44. 通配符匹配 会简单一些。
题目描述
Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*' where:
- '.' Matches any single character.
- '*' Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).
Example 1:
Input: s = "aa", p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".
Example 2:
Input: s = "aa", p = "a"
Output: true
Explanation: '' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".
Example 3:
Input: s = "ab", p = "."
Output: true
Explanation: "." means "zero or more (*) of any character (.)".
Example 4:
Input: s = "aab", p = "cab"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".
Example 5:
Input: s = "mississippi", p = "misisp*."
Output: false
Constraints:
- 0 <= s.length <= 20
- 0 <= p.length <= 30
- s contains only lowercase English letters.
- p contains only lowercase English letters, '.', and '*'.
- It is guaranteed for each appearance of the character '*', there will be a previous valid character to match.
解题思路
这种两个字符串的问题,绝大多数都直接 DP,开个二位数组来做。
我们定义 dp[i][j]
表示 s[:i]
与 p[:j]
是否匹配,则 dp[len(s)][len(p)]
就是最终要求的答案。
我们在每一维多开一个元素的空间,用于表示字符串为空的时候的匹配。初始化的时候需要考虑的情况:
- 若 s 和 p 都为空,dp[0][0] 为 true;
- 若 s 为空、p 非空,dp[0][j] 可能 true 可能 false,参考 Example 4,当 p 为连续的 "a" 或者 "." 的时候为 true;
- 若 s 非空、p 为空,dp[i][0] 为 false;
当 s 和 p 都非空的时候,开始状态转移:
- 若 sc == pc,s 和 p 往前看一位即可,dp[i][j] = dp[i-1][j-1];
- 若 pc == '.',当前最后一位仍然匹配,dp[i][j] = dp[i-1][j-1];
- 若 pc == '',则需要考察前一位的情况,'' 可以匹配 0 位、1 位、或多位:
- 若 ppc == sc 或者 ppc == '.',以下条件满足其一即可匹配:
- "a" 或者 "." 匹配 0 次;
- "a" 或者 "." 匹配 "a" 1 次;
- "a" 或者 "." 匹配 "aaa" 多次;
- 若 ppc != sc,只能匹配 0 位,dp[i][j] = dp[i][j-2];
- 若 ppc == sc 或者 ppc == '.',以下条件满足其一即可匹配:
条件很多很复杂,需要一条一条仔细实现。尤其是匹配多次的情况,可以通过递归推导来实现:若 s[:i] 与 p[:j] 可以匹配2次及以上,则 s[:i-1] 与 p[:j] 也是匹配的。
参考代码
/*
* @lc app=leetcode id=10 lang=cpp
*
* [10] Regular Expression Matching
*/
// @lc code=start
class Solution {
public:
bool isMatch(string s, string p) {
int ns = s.size();
int np = p.size();
// initialize dp[][]
vector<vector<bool>> dp(ns+1, vector<bool>(np+1));
dp[0][0] = true; // empty string match empty pattern
for (int i=1; i<=ns; i++) {
dp[i][0] = false; // non-empty string match empty pattern
}
for (int j=2; j<=np; j+=2) {
if (p[j-1] == '*') {
dp[0][j] = dp[0][j-2]; // empty string match non-empty pattern
} else {
break;
}
} // case "aab" "c*a*b"
for (int i=1; i<=ns; i++) {
for (int j=1; j<=np; j++) {
if (s[i-1] == p[j-1] || p[j-1] == '.') {
dp[i][j] = dp[i-1][j-1];
} else if (p[j-1] == '*') {
if (s[i-1] == p[j-2] || p[j-2] == '.') {
// match 0, 1, or n char
dp[i][j] = dp[i][j-2] || dp[i][j-1] || dp[i-1][j];
} else {
dp[i][j] = dp[i][j-2];
}
} else {
dp[i][j] = false;
}
}
}
return dp[ns][np];
}
};
// @lc code=end
其他解法
这篇博客 提供了两种简洁的递归解法,可以对比参考。