一、递归
一、前提:
1、大问题变小问题
2、找到终止条件
二、如果把大问题变成小问题呢?
- 当前问题是str字符串和pattern字符串匹配
-
最小的情况是两边都为空, 其次是两边各有1个字符, 再其次是两边都有字符。 而以上最小的情况分两类,前两种情况可以作为终止条件,其中若str为空pattern不为空需要进行计算("","a* a* a*"),而最后一种情况可以继续进行递归。
-
接下来需要判断如何递归,如果通过判断pattern当前字符进行划分,那么当前字符是* 号接下来就无从下手了,那我们就需要至少两个字符开始考虑。
- 如果第2个字符不为星号,那么只需判断str和pattern当前的字符是否匹配,匹配则进行下一个字符匹配,不匹配就return false。
- 如果第2个字符为星号,我们就可以选择忽略当前的匹配(a,a* a),匹配一次(a,a*),匹配多次(aa,a*),直接看代码就看得懂。
-
已解决超时问题: "aaaaaaaaaaaaab","aaaaaaaaaac"
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
bool match(string str, string pattern) {
// write code here
return to_match(str,pattern);
}
bool to_match(string str,string pattern){
if(0==str.size()&&0==pattern.size()){
return true;
}
if(str.size()&&pattern.empty()){
return false;
}
if(str.empty()&&2<=pattern.size()&&pattern[1]=='*'){
return to_match(str,pattern.substr(2));//("","a*a*")
}
//下一个是星号
if(2<=pattern.size()&&pattern[1]=='*'){
if((pattern[0]==str[0]||pattern[0]=='.')&&!str.empty()){
return
//匹配0次,或说忽略此次的*("a", ".*a")
to_match(str,pattern.substr(2))
//处理多个字符的(aa,a*)和上一个函数结合就能处理1个字符,比如先执行第二个函数再执行第一个函数
|| to_match(str.substr(1),pattern);
// 如果加上这个只处理1个字符的情况会超时
//|| to_match(str.substr(1),pattern.substr(2))//(ab,a*b)
}
// 有星号,但字符不相等("aab","c*a*b")
return to_match(str,pattern.substr(2));//
}
//没有星号
if(!str.empty()&&(pattern[0]==str[0]||pattern[0]=='.')){
return to_match(str.substr(1),pattern.substr(1));
}
return false;
}
};
二、动态规划
1、公式的定义
dp[m][n] 表示str和pattern的匹配情况, dp[0][0] 表示空串空正则, dp[1][1] 表示str第一个字符和pattern第一个字符进行匹配。
dp[m][n] 为最终的匹配结果,为1则匹配成功。
2、初始条件
空串空正则(str="",pattern="")为true
空串非空正则, 需要计算(str="",pattern="a* a* a*")
非空串空正则(str="a",pattern="")为false
非空串非空正则, 需要计算
1、递推公式
1、当前str和(当前pattern匹配||pattern[j]=='.'),则dp[i][j]=dp[i-1][j-1] (即考虑str前i-1个字符和pattern前j-1个字符是否匹配)
2、当前pattern为星号,则需要考虑几种情况,星号前面的字符能匹配几次
- 如果匹配0次,就是不考虑星号前面字符,即dp[i][j]=dp[i][j-2]
- 如果匹配1次,就要判断星前字符是否和str当前字符相等,如果相等,则dp[i][j]=dp[i][j-1] (相当于延申了dp[i][j-1],如果是不相等的,dp[i][j-1]也应该为0),这个dp[i][j]不仅可以解决(a,a*)的情况,还为多次匹配打基础了。
- 如果匹配多次(可以先认为是匹配第二次),也要判断星前字符是否和str当前字符相等,如果相等,像匹配1次一样,dp[i][j]按道理也是等于dp[i][j-1],但你会发现在(aa,a*)匹配过程中,dp[i][j-1]==0,为什么呢?因为此时的dp[i][j]不是匹配一次的情况,匹配一次才是从左边延申,而匹配第2次的时候,它已经是从匹配了第一次的地方延申,即dp[i-1][j] (当前dp[i][j]的头顶上,即匹配一次时若成功,则* 字符延申过来),结果就是dp[i][j]=dp[i-1][j],相当于判断少1次匹配时是否成功,如果成功,加上当前也相等,则dp[i][j]==1,就匹配成功。
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @param pattern string字符串
* @return bool布尔型
*/
bool match(string str, string pattern) {
// write code here
int m = str.size(),n = pattern.size();
if(m!=0&&n==0)return false;
vector<vector<int> > dp(m+1,vector<int>(n+1,0));
dp[0][0]=1;
for(int i=2;i<=n;i+=2){
dp[0][i]=dp[0][0];//空串,非空正则 ("","a*a*a*")
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(str[i-1]==pattern[j-1]||pattern[j-1]=='.'){
dp[i][j]=dp[i-1][j-1];continue;
}
//有星的情况
if(j>2&&pattern[j-1]=='*'){
dp[i][j]|=dp[i][j-2];//不匹配当前的
}
if(j>=2&&pattern[j-1]=='*'&&(pattern[j-2]==str[i-1]||pattern[j-2]=='.')){
dp[i][j]|=dp[i][j-1];//只匹配1次(a*,a)
}
if(j>=2&&pattern[j-1]=='*'&&(dp[i-1][j]&&pattern[j-2]==str[i-1]||pattern[j-2]=='.')){
dp[i][j]|=dp[i-1][j];//匹配多次(a*,aa)
}
}
}
return dp[m][n];
}
};
时间和空间复杂度:O(MN)