【每日算法】LeetCode 10 —— 正则表达式匹配(一百零二)

题目内容

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。

1、’.’ 匹配任意单个字符
2、’*’ 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

示例

示例 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

提示

0 <= s.length <= 20
0 <= p.length <= 30
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *
保证每次出现字符 * 时,前面都匹配到有效的字符

题解

本题采用动态规划的方式求解。
一、状态表示 f[i,j]
1、集合
所有s[1-i],p[1-j]的全部匹配方式。
2、属性
是否存在一个合法方案,布尔值
二、状态计算
1、p[j] ≠ ‘*‘, f(i,j) = (s[i] == p[j] || p[j] = ‘.’) && f(i-1,j-1))
2、p[j] = ‘*‘, 这里需要枚举*到底表示几个字符。从表示1个字符开始以此类推。公式如下:
f(i,j) = f(i,j-2) || (f(i-1,j-2) && s[i] == p[j]) || (f(i-2,j-2) && s[i] == p[j] && s[i-1] == p[j-1]) || …
最终,优化如下:
f(i,j) = f(i,j-2) || (f(i-1,j) && s[i] == (p[j] || p[j] == ‘.’))

代码

class Solution {
public:
bool isMatch(string s, string p) {
int n = s.size(), m = p.size();
s = ' ' + s, p = ' ' + p; //为了让s和p的下标从1开始。
vector<vector<bool>> f(n+1,vector<bool>(m+1)); //定义状态转移方程
f[0][0] = true;
for(int i = 0; i <= n; i++){
for(int j = 1; j <= m; j++){ //两个空串的匹配在初始化已经赋值为true,第二个字符串从1开始是也是因为空串匹配情况已经排除了
if(j + 1 <= m && p[j+1] == '*' ) continue; //将类似 a* 的情况中遍历到a的情况略过,因为a和*要看成整体,任何一个小部分都不可单独存在。
if(i && p[j] != '*'){ //i要从1开始,因为i==0时,不表示任何字符,且i-1会越界
//这是本题中的第一种情况
f[i][j] = f[i-1][j-1] && (p[j] == '.' || s[i] == p[j]);
}else if(p[j] == '*' ){
//这里说个问题,如果在if中加入判断i是否为0的条件,那么只会走上面的步骤,所以要把判断i放到公式中判断
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: Frederic Niu
Link: https://www.fredericniu.cn/2021/04/05/【每日算法】LeetCode-10-——-正则表达式匹配(一百零二)/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
我的公众号