剑指 Offer 19. 正则表达式匹配 (偏难)- 7月3日

题目

我的思路

我的解决思路是深搜递归,可是需要注意时间复杂度,不同的深搜3^n与2^n的复杂度差别还是蛮大的。

下面结合官方题解,阐述一下两种思路:

思路一:深搜递归

时间复杂度是2^n,空间复杂度是n(可能同时存在n次递归调用层)。

class Solution {
public:
    bool isMatch(string s, string p) {
        if(p.empty()) return s.empty();
        if(p[1] == '*'){
            return isMatch(s, p.substr(2)) || (!s.empty() && (s[0] == p[0] || p[0] == '.')) && isMatch(s.substr(1), p);
        }
        else{
            return !s.empty() && (s[0] == p[0] || p[0] == '.') && (isMatch(s.substr(1), p.substr(1)));
        }
    }
};

作者:jarvis1890
链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/solution/zheng-ze-biao-da-shi-pi-pei-di-gui-qiu-jie-by-jarv/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路二:动态规划(神!)

 
class Solution {
    public boolean isMatch(String A, String B) {
        int n = A.length();
        int m = B.length();
        boolean[][] f = new boolean[n + 1][m + 1];

        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                //分成空正则和非空正则两种
                if (j == 0) {
                    f[i][j] = i == 0;
                } else {
                    //非空正则分为两种情况 * 和 非*
                    if (B.charAt(j - 1) != '*') {
                        if (i > 0 && (A.charAt(i - 1) == B.charAt(j - 1) || B.charAt(j - 1) == '.')) {
                            f[i][j] = f[i - 1][j - 1];
                        }
                    } else {
                        //碰到 * 了,分为看和不看两种情况
                        //不看
                        if (j >= 2) {
                            f[i][j] |= f[i][j - 2];
                        }
                        //
                        if (i >= 1 && j >= 2 && (A.charAt(i - 1) == B.charAt(j - 2) || B.charAt(j - 2) == '.')) {
                            f[i][j] |= f[i - 1][j];
                        }
                    }
                }
            }
        }
        return f[n][m];
    }
}

作者:jerry_nju
链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/solution/zhu-xing-xiang-xi-jiang-jie-you-qian-ru-shen-by-je/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

时间复杂度是m*n,空间复杂度也是m*n

我的实现

class Solution {
public:
    //动态规划:f[i][j]表示主串的前i+1(0~i)个字符能否由模式串的前j+1(0~j)个字符匹配
    /*
    模式串p[j]是'*',那么f[i][j]=f[i][j-2]||f[i][j-1]||(s[i]==s[i-1]&&(f[i-1][j-1]||f[i-1][j]))||(p[j-1]=='.'&&(f[i-1][j-1]||f[i-1][j]))
    模式串p[j]是'.',那么f[i][j]=f[i-1][j-1];
    模式串p[j]是others,那么f[i][j]=f[i-1][j-1]&&s[i]==p[j]
    */
    bool isMatch(string s, string p) {
        //给模式字符串去重
        //bool[][] f = new bool[][](s.size()+1,p.size()+1);
        bool f[s.size()+1][p.size()+1];
        f[0][0]=true;
        //return f[0][0];
        for(int j = 1;j<=p.size();j++){
            if(j==1)f[0][j]=false;
            else if(j>=2){
                if(p[j-1]=='*'){
                    f[0][j] = f[0][j-2]||f[0][j-1];
                    //cout<<f[0][j]<<"	j:"<<j<<endl;
                }else{
                    f[0][j] = false;
                }
            }
        }
        for(int i = 1;i<=s.size();i++){
            f[i][0]=false;
        }
        
        for(int i = 1;i<=s.size(); i++){
            for(int j = 1; j<=p.size();j++){
                //cout<<"check???"<<endl;
                if(p[j-1]=='*'){
                    if(i==1)f[i][j]=f[i][j-2]||f[i][j-1]||(p[j-2]=='.'&&(f[i-1][j-1]||f[i-1][j]));else
                    f[i][j]=f[i][j-2]||f[i][j-1]||(s[i-2]==s[i-1]&&s[i-1]==p[j-2]&&(f[i-1][j-1]||f[i-1][j]))||(p[j-2]=='.'&&(f[i-1][j-1]||f[i-1][j]));
                }else if(p[j-1]=='.'){
                    f[i][j]=f[i-1][j-1];
                }else {
                    f[i][j] = f[i-1][j-1]&&s[i-1]==p[j-1];
                }
            }
        }
        //cout<<f[0][0]<<f[0][1]<<f[0][2]<<"
"<<f[1][0]<<f[1][1]<<f[1][2]<<"
"<<f[2][0]<<f[2][1]<<f[2][2]<<endl;
        int a = s.size();
        int b = p.size();
        return f[a][b];
    }
};
/*
我的做法通过了部分测试用例,但是时间复杂度太高了,遇到仅最后一个字符不能匹配,而的字符前面存在多种匹配方式的用例,会发生超时。
比如:"aaaaaaaaaab" "a*a*a*a*a*a*a*a*a*a*c"
经过改进(在深搜递归出口处增加一点判断),深搜的复杂度从3^n降到了2^n通过了所有用例。

深搜
每次读取两个字符,如果被读取的第二个字符不是*那么回退一个字符。
6种情况:第一个字符是否是. ,第二个字符是否是* ,第一个字符是否与待匹配字符相同。
否否否,返回false;
否否是,continue;
`否是否,continue;
`否是是,深搜continue/深搜指针再回退一个字符continue;
是否,continue;
是否,不存在(continue);
`是是,深搜指针再回退一个字符,并把‘.’改成待匹配字符,continue
class Solution {
public:
    bool search(string &s,string &p,int s_it,int p_it){
        if(s.size()==s_it ){
            if(p_it==p.size())return true;
            }
        if(p.size()==p_it)return false;
        cout<<"s:"<<s[s_it]<<"	p:"<<p[p_it]<<endl;
        if(p.size()-1>p_it){//如果存在两个未匹配的模式字符
            if(p[p_it+1]=='*'){//如果第二个字符是*
                if(p[p_it]=='.'){//是是
                    if(search(s,p,s_it+1,p_it+2)==true)return true;
                    if(search(s,p,s_it+1,p_it)==true)return true;
                    if(search(s,p,s_it,p_it+2)==true)return true;
                }else{
                    if(p[p_it] == s[s_it]){//否是是
                        if(search(s,p,s_it+1,p_it+2)==true)return true;
                        if(search(s,p,s_it+1,p_it)==true)return true;
                        if(search(s,p,s_it,p_it+2)==true)return true;
                    }else{//否是否
                    return search(s,p,s_it,p_it+2);
                    }
                }
            }
        }
        {//如果只有一个未匹配的模式字符 or 如果第二个字符不是*
            if(p[p_it]!='.'){
                if(p[p_it]!=s[s_it]) //否否否
                    return false;
                else//否否是
                    return search(s,p,s_it+1,p_it+1);
            }else{//是否
                return search(s,p,s_it+1,p_it+1);
            }
        }
    }
    bool isMatch(string s, string p) {
        //给模式字符串去重
        for(int i = 0;i<s.size();i++){
        }
        return search(s,p,0,0);
    }
};

下面这个深搜不会发生超时
class Solution {
public:
    bool search(string &s,string &p,int s_it,int p_it){
        if(s.size()==s_it ){
            if(p_it==p.size())return true;
            if((p.size()-p_it)%2!=0){
                return false;
            }else{
                int k;
                for(k=p_it+1;k<p.size();k+=2){
                    if(p[k]!='*')return false;
                }
                return true;
            }
            }
        if(p.size()==p_it)return false;
        //cout<<"s:"<<s[s_it]<<"	p:"<<p[p_it]<<endl;
        if(p.size()-1>p_it){//如果存在两个未匹配的模式字符
            if(p[p_it+1]=='*'){//如果第二个字符是*
                if(p[p_it]=='.'){//是是
                    //if(search(s,p,s_it+1,p_it+2)==true)return true;
                    if(search(s,p,s_it+1,p_it)==true)return true;
                    if(search(s,p,s_it,p_it+2)==true)return true;
                }else{
                    if(p[p_it] == s[s_it]){//否是是
                        //if(search(s,p,s_it+1,p_it+2)==true)return true;
                        if(search(s,p,s_it+1,p_it)==true)return true;
                        if(search(s,p,s_it,p_it+2)==true)return true;
                    }else{//否是否
                    return search(s,p,s_it,p_it+2);
                    }
                }
            }
        }
        {//如果只有一个未匹配的模式字符 or 如果第二个字符不是*
            if(p[p_it]!='.'){
                if(p[p_it]!=s[s_it]) //否否否
                    return false;
                else//否否是
                    return search(s,p,s_it+1,p_it+1);
            }else{//是否
                return search(s,p,s_it+1,p_it+1);
            }
        }
    }
    bool isMatch(string s, string p) {
        //给模式字符串去重
        return search(s,p,0,0);
    }
};

*/

拓展学习

加快速度,刻意练习速度!一定要在上午12点之前弄完每日一题

原文地址:https://www.cnblogs.com/BoysCryToo/p/13426614.html