Leetcode–10. 正则表达式匹配(动态规划) [2020.9.10]
前言
这题一开始我是打算把各种情况if-else的角度穷举出来,做到后面缴枪投降,后来参考了别人的解答才发现这是一道动态规划的问题。唉,dp问题就是我的梦魇,经常无法判断这道题是否为dp,感觉还是题目做少了,还得多加练习才是。
回到这道题目,对于动态规划问题,最重要的还是要按照dp流程一步一步走:
- (定义): s [ i ] s[i] s[i]和 p [ j ] p[j] p[j]分别为s与p字串第 i i i和 j j j个字母, d p [ i ] [ j ] dp[i][j] dp[i][j]表示 s [ 0.. i ] s[0..i] s[0..i]和 p [ 0.. j ] p[0..j] p[0..j]是否匹配,用true-false表示。
- (初始化)a. 我们易知对于两个空字串必然匹配,则有 d p [ 0 ] [ 0 ] = t r u e dp[0][0]=true dp[0][0]=true。b. 当 i = = 0 , p [ j − 1 ] = = ′ ∗ ′ i==0,p[j-1]=='*' i==0,p[j−1]==′∗′有 d p [ 0 ] [ j ] = d p [ 0 ] [ j − 2 ] dp[0][j]=dp[0][j-2] dp[0][j]=dp[0][j−2](相当于消去 ′ ∗ ′ '*' ′∗′前面字符)
- (状态转移方程)
其中一、二两点是好理解的,这种情况 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j]=dp[i-1][j-1] dp[i][j]=dp[i−1][j−1]。
在第三点中又分为了两种情况:如果 " ∗ " "*" "∗"前面的符号不匹配,那"*“的作用用以消去前面的字符,固有 d p [ i ] [ j ] = d p [ i ] [ j − 2 ] dp[i][j]=dp[i][j-2] dp[i][j]=dp[i][j−2]。
如果 " ∗ " "*" "∗"前面的符号匹配或者为 . . .,此时我们 " ∗ " "*" "∗"可以又三种作用,对前面字符产生消去、单个、多个的效果。
其中多个字符效果 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]可能需要理解一下.举个例子s:”###bbbb" p:"###b*",我们可以看到s中有多个b,如果此时为了匹配*需要产生多个b的效果,就有s:"###bbbb" p:"###bb*",我们让*多产生了一个b,此时也等价于s:"###bbb" p:"###b*",相当于s,p都消去一个b,p没变,而s少了一个b,所以有 d p [ i ] [ j [ = d p [ i − 1 ] [ j ] dp[i][j[=dp[i-1][j] dp[i][j[=dp[i−1][j]
问题
代码
class Solution {
public:
bool isMatch(string s, string p) {
int slen = s.length(), plen = p.length();
if(slen==0&&plen==0)
return true;
vector<vector<bool>> dp(slen+1,vector<bool>(plen+1,false));
dp[0][0]=true; //初始化
for(int i=2; i<plen+1; i++)
if(p[i-1]=='*'&&dp[0][i-2])
dp[0][i]=true;
for(int i=1; i<slen+1; i++){
for(int j=1; j<plen+1; 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(j-2<0)
dp[i][j] = (dp[i][j-1]||dp[i-1][j]);
else{
//注意三种情况有一个true最后结果就为true。
if(p[j-2]!=s[i-1]&&p[j-2]!='.')
dp[i][j]=dp[i][j-2];
else
dp[i][j] = (dp[i][j-2]||dp[i][j-1]||dp[i-1][j]);
}
}
else
dp[i][j]=false;
}
}
return dp[slen][plen];
}
};
附录
虽然能推导出状态转移方程,但是还需要边界以及初始化,太容易出错了。