动态规划之正则表达式问题
leetcode 第10题
首先需要理解题意
确定.可以代表任何数, * 可以代表0个或者1个前面一个字母
这里贴上一个讲解这道题目的油管链接,不得不是印度人在编程这块确实可以
NOTE:
“.*” 其实就是相当于星号可以将dot变成0个或者多个,所以这个组合可以代表任意字符串,包括空字符串
接下来定义 d p [ i ] [ j ] dp[i][j] dp[i][j]
d p [ i ] [ j ] dp[i][j] dp[i][j]表示s的前i-1个能否被p的前j-1个匹配 是一个vector<vector>类型
初始化 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0] 两个空字符串进行正则匹配为真
初始化第一行 d p [ 0 ] [ j ] dp[0][j] dp[0][j] 主要针对 a ∗ b ∗ 、 a ∗ 、 a ∗ b ∗ c ∗ a*b*、a*、a*b*c* a∗b∗、a∗、a∗b∗c∗等情况
需要初始化
例如 a ∗ a* a∗那么第一行需要初始化为【1,0,1】 最后一个1表示的就是 a ∗ a* a∗可以和空的字符串进行正则匹配
代码如下:
int n = s.size();
int m = p.size();
vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
dp[0][0] = true;
//在第0行设置一些true;如果有p是 a* or a*b* or a*b*c*他们均可以匹配 s = ""
for(int j = 1; j <= m; ++j){
if(p[j-1] == '*'){
dp[0][j] = dp[0][j-2]; //例如a*在第一行就需要设置为101
}
}
接下来就是主要的逻辑部分
-
如果 p [ j − 1 ] = = ′ . ′ p[j-1] == '.' p[j−1]==′.′ 或者 p [ j − 1 ] = = s [ j − 1 ] p[j-1] == s[j-1] p[j−1]==s[j−1] 那么显然 d p [ i ] [ j ] dp[i][j] dp[i][j] 的结果与 p [ j − 1 ] 和 s [ j − 1 ] p[j-1]和s[j-1] p[j−1]和s[j−1]无关
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];
-
如果 p [ j − 1 ] = = ′ ∗ ′ p[j-1] == '*' p[j−1]==′∗′
那么又分为两种情况
(1)不考虑$p[j-2] $
如果我不考虑 p [ j − 2 ] p[j-2] p[j−2] 意味着我认为 ∗ * ∗前面的字母 p [ j − 2 ] p[j-2] p[j−2]在 s [ i − 1 ] s[i-1] s[i−1]及之前没有出现
∗ * ∗的意思就是有0个 p [ j − 2 ] p[j-2] p[j−2];
$dp[i][j] = dp[i][j-2]; $
(2) 考虑p[j-2]
如果 p [ j − 2 ] = = ′ . ′ ∣ ∣ p [ j − 2 ] = = s [ i − 1 ] p[j-2] == '.' || p[j-2] == s[i-1] p[j−2]==′.′∣∣p[j−2]==s[i−1]
那么我就可以不考虑 s [ i − 1 ] s[i-1] s[i−1] 直接看 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j] 的情况就可以
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) {
//解题之前先要搞清楚情况,例如ab*c
//可以匹配abc、ac、abbbbbc
//a.c可以匹配abc、aac
//注意".*" 就是*可以使得.变为0个或者多个,所以可以表示任意的字符串(包括空字符串)
//dp[i][j] 表示s的前i-1个能否被p的前j-1个匹配
int n = s.size();
int m = p.size();
vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
dp[0][0] = true;
//在第0行设置一些true;如果有p是 a* or a*b* or a*b*c*他们均可以匹配 s = ""
for(int j = 1; j <= m; ++j){
if(p[j-1] == '*'){
dp[0][j] = dp[0][j-2]; //例如a*在第一行就需要设置为101
}
}
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
if(p[j-1] == '.' || p[j-1] == s[i-1]){
dp[i][j] = dp[i-1][j-1];
}
else if(p[j-1] == '*'){
dp[i][j] = dp[i][j-2]; //如果是*前面的字母p[j-2]在s[i-1]及之前没有出现的情况 就是不考虑字母p[j-2]
if(p[j-2] == '.' || p[j-2] == s[i-1]){//考虑字幕p[j-2],如果是.或者和s[i-1]相等的话,那么就不考虑s[i-1];
dp[i][j] = dp[i][j] | dp[i-1][j]; //两种情况只需要其中一个为真
}
}
else{
dp[i][j] = false;
}
}
}
return dp[n][m];
}
};
最后给一个测试用例大家直观感受一下
s = " a b c d e " s ="abcde" s="abcde"
p = " a ∗ b ∗ c ∗ . " p ="a*b*c*." p="a∗b∗c∗."
“” | a | * | b | * | c | * | . | |
---|---|---|---|---|---|---|---|---|
“” | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
a | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 1 |
b | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
c | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
d | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
e | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |