leetcode10 Regular Expression Matching

这道题我光读题就读了很长时间。题意是判断字符串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;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值