这道题我光读题就读了很长时间。题意是判断字符串s和p是否匹配,s只含有小写字母,p含有小写字母和'.' '*'两个字符
'.' 字符表示任意字符,可以和任意一个字符匹配 '*'表示0个或多个前一个字符,例如a*当中的*表示0个或多个a
所以s="aaa" p="a*" 是匹配的,因为*可以转化成2个a。同理s="b" p="ba*"也是匹配的,因为*可以变成0个a。
这道题的难点就是*可以变成0个或多个前一个字符。如果没有星号的话,直接从头遍历到尾看是否匹配就可以了。所以我们首先讨论带星号的情况。
我个人觉得*的零个字符和非零个字符是一个分界点,我们就讨论星号代表0个前一个字符和非0个前一个字符的区别。首先p的第一个字符不可能是*,如果是s和p一定是不匹配的
星号代表0个字符的情况:
s="b" 和p="a*b"是匹配的,此时等价于s和p[2:]匹配
s=aaab" 和 p="a*b"也是匹配的,此时等价于s和p的第0个字符是匹配的,且s[1:]和p也是匹配的。
所以s和p是否匹配取决于s和p的子串是否匹配,具有最优子结构的特性,我们用DP建立递推公式:
dp[i][j]表示s[i:]和p[j:]是否匹配,dp[s.size()][p.size()]=true dp[x][p.size()]=false(x<s.size())
求解dp[i][j]:
若p[j]==* continue
若p[j]==字母 {
if p[j+1]==* then dp[i][j]=(i<s.size() && p[j]==s[i] && dp[i+1][j]) || dp[i][j+2]
else if i==s.size() then dp[i][j]=false;
else dp[s_index][p_index]=(p[p_index]==s[s_index]) && dp[s_index+1][p_index+1];
}
当p[j]为字母的时候,我们关心的是p[j+1]是否是*,因为如果p[j+1]是星号的话,则当前字符p[j]可能出现0次而不参与匹配,所以如果下一个字符是星号的话,有两种可能:当前字符不出现,s[i:]和p[j+2]匹配 或 (p[j]至少出现一次,且p[j]与s[i]匹配,且s[i+1:]和p[j:]匹配)。
当不满足第一个if的条件的时候,p[j+1]不可能是星号(要么p[j]是最后一个字符,要么p[j+1]是非星号字符,此时如果s[i:]为空,则二者一定不匹配(第二个if)
如果程序执行到了第三个if,则s[i:]一定不为空且p[j+1]一定不为星号,则此时s[i:]和p[j:]匹配等价于(p[j]==s[i]且s[i+1:]和p[j+1:]匹配)
当p[j]=='.'的时候同理,只不过'.'可以匹配任意一个字符,对比p[j]==字母,只需要把判断条件的s[i]==p[j]去掉就可以了。
我写了自底向上和自顶向下两种方案,自顶向下12ms,自底向上16ms
自顶向下:
enum if_match {TRUE,FALSE,NIL};
class Solution {
public:
if_match **dp;
void init(int s_size,int p_size){
dp=new if_match*[s_size];
for(int i=0;i<s_size;++i){
dp[i]=new if_match[p_size];
for(int j=0;j<p_size;++j){
dp[i][j]=NIL;
}
}
}
void finish(int s_size){
for(int i=0;i<s_size;++i){
delete []dp[i];
}
delete []dp;
}
bool isMatch(string s, string p) {
init(s.size(),p.size());
bool ret=isMatch(s,0,p,0);
finish(s.size());
return ret;
}
bool isMatch(const string &s,int s_index,const string &p,int p_index);
};
//计算dp[s_index][p_index]
bool Solution::isMatch(const string &s,int s_index,const string &p,int p_index){
//cout<<"s_index="<<s_index<<" p_index="<<p_index<<endl;
//p不可能以*开头
if(p_index>=p.size()){
return s_index>=s.size();
}
//p没有到头
if(s_index<s.size()){
if(dp[s_index][p_index]!=NIL){
return dp[s_index][p_index]==TRUE?true:false;
}
}else{
if(p_index+1<p.size() && p[p_index+1]=='*') return isMatch(s,s_index,p,p_index+2);
else return false;
}
bool first_match=(s_index<s.size()) &&
((p[p_index]=='.')||(p[p_index]==s[s_index]));
if( p_index+1<p.size() && p[p_index+1]=='*'){
dp[s_index][p_index]=
((isMatch(s,s_index+1,p,p_index) && first_match)
||isMatch(s,s_index,p,p_index+2))?TRUE:FALSE;
return dp[s_index][p_index]==TRUE?true:false;
}else{
dp[s_index][p_index]=(first_match&&isMatch(s,s_index+1,p,p_index+1))?TRUE:FALSE;
return dp[s_index][p_index]==TRUE?true:false;
}
}
自底向上:
class Solution {
public:
bool **dp;//dp[i][j]:s[i]到s最后和p[j]到p最后是否匹配
void init(string &s,string &p){
dp=new bool*[s.size()+1];
for(int i=0;i<s.size()+1;++i){
dp[i]=new bool[p.size()+1];
}
for(int i=0;i<s.size();++i) dp[i][p.size()]=false;
dp[s.size()][p.size()]=true;
}
bool isMatch(string s, string p) {
init(s,p);
if(p[0]=='*') return false;
for(int p_index=p.size()-1;p_index>=0;--p_index){
if(p[p_index]=='*') continue;
else if(p[p_index]=='.'){
for(int s_index=s.size();s_index>=0;--s_index){
if(p_index+1<p.size()&&p[p_index+1]=='*')
dp[s_index][p_index]=(s_index<s.size() && dp[s_index+1][p_index])
|| dp[s_index][p_index+2];
else if(s_index==s.size()){
dp[s_index][p_index]=false;
}else{
dp[s_index][p_index]=dp[s_index+1][p_index+1];
}
//cout<<"s_index="<<s_index<<" p_index="<<p_index<<" dp[s_index][p_index]="<<dp[s_index][p_index]<<endl;
}
}else{
for(int s_index=s.size();s_index>=0;--s_index){
if(p_index+1<p.size() && p[p_index+1]=='*')
dp[s_index][p_index]=(s_index<s.size() && p[p_index]==s[s_index] && dp[s_index+1][p_index])
|| dp[s_index][p_index+2];
else if(s_index==s.size()){
dp[s_index][p_index]=false;
}else{
dp[s_index][p_index]=(p[p_index]==s[s_index]) && dp[s_index+1][p_index+1];
}
//cout<<"s_index="<<s_index<<" p_index="<<p_index<<" dp[s_index][p_index]="<<dp[s_index][p_index]<<endl;
}
}
}
bool ret=dp[0][0];
finish(s);
return ret;
}
void finish(string &s){
for(int i=0;i<s.size()+1;++i) delete []dp[i];
delete []dp;
}
};