题目传送门:https://leetcode-cn.com/problems/regular-expression-matching/
看懂标准题解后AC了此题,看的时候觉得很难理解,这里我尝试解释得更通俗易懂一些。
题解用了动态规划的思想,即直接求解一定规模的问题很难思考,但该问题可以转化为一个或几个更小规模的同类问题去解决。
我们使用 bool dp[i][j] 来代表 s 串的前 i 个字符是否与 p 串的前 j 个字符相匹配,注意,这里是前 i 个字符,即 dp[i][j] 是 s[0 ... i - 1] 与 p[0 ... j - 1] 是否匹配。
该问题可以分成以下两种情况:
1: p 串最后一个字符不是 * 的情况:
此时我们只需考虑 s[i - 1] 与 p[j - 1] 是否匹配即可(注意,这里的匹配有两层含义:s[i - 1] == p[j - 1] 或者 p[j - 1] == .)。如果匹配的话,则dp[i][j] = dp[i - 1][j - 1].
参考如下代码:
if(p[j - 1] != '*')dp[i][j] = (match(i, j) ? dp[i - 1][j - 1] : false);
2: p串最后一个字符是 * 的情况:
此时又可以分成两种情况:
- 这个 * 当 0 个前面的字符使用:此时这个 * 和前面的字符相当于都没了,dp[i][j] = dp[i][j - 2].
- 这个 * 当任意个( > 0) 前面的字符使用:此时,如果有s[i - 1] 与 p[j - 2] 匹配,则 dp[i][j] = dp[i - 1][j].(这里可能会有疑问,比如我:不应该是 dp[i][j] = dp[i - 1][j - 1]吗?因为 p 串的最后一个字符 * 被匹配走了呀。 答:这个 * 不一定只匹配了 s 的最后一个字符,有可能 s 的倒数第二个、倒数第三个....都是这个 * 匹配的,所以这里 dp[i][j] = dp[i - 1][j] 是合理的。)
参考如下代码:
if(match(i, j - 1))dp[i][j] = dp[i - 1][j] || dp[i][j - 2]; else dp[i][j] = dp[i][j - 2];
完整代码如下:
- class Solution {
- public:
- bool isMatch(string s, string p) {
- int s_len = s.size(), p_len = p.size();
- function<bool(int, int)> match = [&](int i, int j){
- if(!i)return false;//这里不用管 j 是因为遍历数组时 j 从 1 开始
- return p[j - 1] == '.' || s[i - 1] == p[j - 1];
- };
- vector<vector<int>> dp(s_len + 1, vector<int>(p_len + 1));
- dp[0][0] = true;
- for(int i = 0; i <= s_len; i++){
- for(int j = 1; j <= p_len; j++){
- if(p[j - 1] != '*')dp[i][j] = (match(i, j) ? dp[i - 1][j - 1] : false);
- else{
- if(match(i, j - 1))dp[i][j] = dp[i - 1][j] || dp[i][j - 2];
- else dp[i][j] = dp[i][j - 2];
- }
- }
- }
- return dp[s_len][p_len];
- }
- };