题目
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖整个字符串s的,而不是部分字符串。
提示:
1 <= s.length <= 20
1 <= p.length <= 30
s 只含小写英文字母。
p 只含小写英文字母,以及字符 . 和 *。
保证每次出现字符 * 时,前面都匹配到有效的字符
思路
- 题目中要求s与p正则表达式匹配,实际是指p经过正则表达式表示后字符串的某一种可能性与s字符串的字符刚好一一对应。(第一次提交没理解,报错)
- “ * ” 需要与前面一字符融为一体。以 “a*”为例,匹配零个或多个前面的那一个元素指的是"a*"表达式可能含有0个a,也可能含有多个a。即a*含有n个a(0<=n)。
此时最直接的办法是,将p字符串的所有种情况枚举出来(主要是*可能产生的字符的数量),然后与s进行对比,如果有一种可能性两字符串刚好相等,则可输出true。
但此时我们可以发现,枚举每一种可能性时,中间可能出现重复部分(即p字符串中某些字段可能在不同可能性中均含有,会出现计算现象)。此时动态规划便可以引出来应用。
在长度为len的p字符串中,他的最好的一种表达结果是与s相同,比如
s="asdf", p="asd."
即当.为f时,p与s相同
但前提是.前面的asd字符段需要与s前面的也相等
因此字符串索引值靠后的表达结果受到他前面结果的影响
所以此时动态规划需要从字符串左向右进行,因此存储前面的状态(标记现在为止是否与s字符串一致)
我们可以建立一个二维数组f存储状态,f[i][j]表示s的前i个元素是否和p的前j个元素一致。(注意:s[i-1]是s第i个元素,p[j-1]是p第j个状态,写代码时不要混淆。至于为什么不将设置为i、j代表s、p的i、j下标之前的元素,后面会讲)
此时我们需要考虑两种情况:
- p[j-1]不等于"*"
- p[j-1]等于"*"
1、 当 p[j-1]不是“*”,分为两种情况:
-
p[j-1]和s[i-1]相等或者p[j-1]为".":他们能保证当前元素相同,但还需知道前面元素是否一致。即需要参考f[i-1][j-1]的状态,如果前面的状态是与s不匹配的,那在这个位置f[i][j]也是不能匹配的,为false。
-
不是上述情况:说明此时元素不匹配s[i-1]!=p[j-1],则f[i][j]状态应该代表不匹配,为false
状态方程如下:
2、当 p[j-1]是“*”,分为两种情况:
-
p[j-2]和s[i-1]不相等,此时“*”代表0个前一字符。相当于p[j-1]与p[j-2]相当于空气,并不存在。所以此时f[i][j] 的状态应该和f[i][j-2]一致(即p的前j-2个字符,到p[j-3]为止)
-
p[j-2]和s[i-1]相等或者p[j-2]为".",因为“*”可以表示0或者多个前一字符,所以这里又分为两种情况:
- “*”表示0个字符:此时同p[j-2]和s[i-1]不相等一样,p[j-1]与p[j-2]相当于空气。此时f[i][j] 的状态应该和f[i][j-2]一致
- “*”表示多个字符:
- 当 "*"表示一个字符时(如“a*”表示“a”):f[i][j]=f[i−1][j]
- 当 "*"表示2个字符时(如“a*”表示“aa”):f[i][j]=f[i−2][j]
- 当 "*"表示3字符时(如“a*”表示“aaa”):f[i][j]=f[i−3][j]
- 此时要分好多种情况,比较复杂,但是我们可以发现规律:因为j是定值,所以对于i-n(i-1>=n>=0),有f[i-n][j]受到f[i-n-1][j]的影响,说明只要f[1][j]状态匹配,f[i][j]则都匹配。所以f[i][j]与f[i-1][j]状态相关,与f[1][j]状态也相关。
- 因为“*”有这么多种情况,所以只要有一种情况能匹配就满足要求,因此f[i][j]状态可以取上述情况的或情况
状态方程如下:
我们可以发现,里面有i-1,j-2,如果将i、j设为下标,数组访问会出现溢出现象。因此我们需要从1开始。
因为每一个状态都需要前面状态决定,所以在f[i][j]中,i=0的行和j=0的列是本题动态规划的起点。我们也需要对其进行初始化
- 首先将f[0][0]设为true,因为此时的意思是s的前0个元素和p的前0个元素是否匹配,既然都没有元素,那肯定是匹配的,所以设为true
- f[0][j]其他元素:此时的意思是s的前0个元素和p的前j个元素是否匹配,s此时没有元素要匹配,p[j]只要有元素,都不是和s匹配的,需要设为false。但是含“*”的位置可代表0个元素,所以是可以和此时没有元素的s匹配。此判断过程可以和上面s和p内元素匹配程序合并。
- f[i][0](此处i>0):因为此时的意思是s的前i个元素和p的前0个元素是否匹配,因此p中此时没有元素所以不可能匹配,全都为false(0)。初始化数组时就建立了,无需重新赋值。
代码
class Solution {
public:
bool isMatch(string s, string p) {
int len1 = s.size(), len2 = p.size();
vector<vector<int>> f(len1+1, vector<int>(len2+1));
auto match = [&](int i, int j){
if(i==0)
return false;
if(s[i-1]==p[j-1]||p[j-1]=='.')
return true;
else
return false;
};
f[0][0] = true;
for(int i=0;i<=len1;i++){
for(int j=1;j<=len2;j++){
if(p[j-1]=='*'){
if(match(i, j-1))
f[i][j] = f[i-1][j];
f[i][j] |= f[i][j-2];
}
else if(match(i, j))
f[i][j] = f[i-1][j-1];
else
f[i][j] = false;
}
}
return f[len1][len2];
}
};