LeetCode44--Wildcard Matching

这一题和第10题的匹配其实思路大致一致,不过需要做一点点优化,不然会超时。

大致的思路还是从后往前匹配,这里的’?‘可以匹配任何的字母,相当于前一题的’.’,而 '*'则是匹配空串,或者任意串。任意串的话,则是以当前位置结束的任意串。
故当出现星号时,能否匹配取决于这个星号匹配掉待匹配串当前位置往前k个字符组成的串后(可以理解成抵消了这个位置及前面的k个字符),剩余的待匹配串是否能够与星号前的串匹配。有了这个思路后,第一反应就是得到待匹配串的位置,然后循环去计算星号抵消i个值是否能够得到匹配,若中途出现了匹配,自然就可以,若到最后都没法匹配,则说明这个星号无法做到匹配,GameOver!

那么如果这么想,就掉到它的陷阱中去了,你多做了很多次运算!
我们假设你在计算待匹配串l1和模式串l2的时候出现了*号,我们来假设一种极端情况:它连续好多个都是 星号。首先第一轮大家都不抵消,算到某个值的时候我们发现不匹配了,我们开始回溯,然后我们最后一个 星号 开始抵消待匹配串中的字符,使得在某个位置前被抵消了一定次数不再匹配,因为此方案最终无法匹配,故期间我们算的所有位置是无法匹配的;然后我们回溯我们这个星号继续多抵消1个,从而再次去计算看是否能匹配,结果还是不能匹配,所以中间的都做不到匹配;我们这个星号回溯完后,会返回到上一个星号去,我们上一个星号也开始发挥能力,抵消1个,然后我们第一个星号又苦逼的从0个开始遍历。这个时候我们实际上就做了很多重复的操作了。比如之前星号抵消4个和第二个星号抵消0个,与之前星号抵消3个第二个星号抵消1个…期间由于 星号 的不同组合,某个位置(p,q)是否匹配被算了很多很多次。

是不是看不懂???看不懂那就看能不能理解这句话:某个数可以被多组两个数组成(100 = 1+ 99 = 2+98 = 3 + 97 …)
当最右边的两个连在一起的星号分别取0 1 和 1 0时(这里表示抵消的字符数目),它们左边的所有字符都被算了两次!

那我们怎么避免这个重复计算呢?我们多用点空间来储存(l1,l2)的匹配结果就好啦,比如l1 = 7,l2 = 9 表示待匹配串第7个位置及左边的串是否能够与模式串第9个位置及左边的串匹配即可。当之后再次来判断的时候,我们无需重新计算一次,只需要根据已经算好的值返回即可。
好啦,Show Code

#include<cstdlib>
#include<cstring>
class Solution {
public:
    bool CanMatch(string &s,string &p,int l1,int l2,int **flag)
    {
        if(l1 < 0 && l2 < 0)//同时结束了
            return true;
        if(l1 >= 0 && l2 < 0)//如果第一个字符串未结束,第二个已经结束了,返回失败
            return false;
        if(l1 < 0 && l2 >= 0)//第一个结束了,第二个还未结束  若l2全为*则为true,否则为false
        {
            while(l2 >= 0)
                if(p[l2--] != '*')
                    return false;
            return true;
        }
        if(flag[l1][l2] != -1)
        {
            //已经计算了
            return flag[l1][l2] == 1;//若为1,返回true,否则返回false
        }
        //若p这个不是?,也不是*,
        if(p[l2] != '?' && p[l2] != '*')
        {
            if(s[l1] != p[l2])
            {
                flag[l1][l2] = 0;
                return false;
            }
            if(CanMatch(s,p,l1-1,l2-1,flag))
            {
                flag[l1][l2] = 1;
                return true;
            }
            flag[l1][l2] = 0;
            return false;
        }
        else
        {
            if(p[l2] == '?')
            {
                if(CanMatch(s,p,l1-1,l2-1,flag))
                {
                    flag[l1][l2] = 1;
                    return true;
                }
                flag[l1][l2] = 0;
                return false;
            }
                
            else//如果这个是*号,则可能匹配剩余长度为l1的字符串
            {
                //若匹配长度为0的,则CanMatch(s,p,l1-0,l2-1);
                //若匹配长度为1的,则CanMatch(s,p,l1-1,l2-1)
                //若匹配长度为2的,则CanMatch(s,p,l1-2,l2-1)
                for(int i = 0;i <= l1+1;++i)
                    if(CanMatch(s,p,l1-i,l2-1,flag))
                        return true;
                flag[l1][l2] = 0;
                return false;
            }
        }
        flag[l1][l2] = 0;
        return false;
    }
    bool isMatch(string s, string p) {
        //从后往前匹配,当无法匹配时,则说明失败了
        //若是?  则跳过这个字符继续匹配
        //若p全为*,则成功
        if(s.size() == 0)//若s为空字符串且p全为*,则true
        {
            for(int i = 0;i < p.size();++i)
                if(p[i] != '*')
                    return false;
            return true;
        }
        else if(p.size() == 0)
            return false;
        int **flag;
        flag = (int **)malloc(sizeof(int*)*s.size());
        for(int i = 0;i < s.size();++i)
        {
            flag[i] = (int *)malloc(sizeof(int)*p.size());
            for(int j = 0;j < p.size();++j)
                flag[i][j] = -1;
        }
        return CanMatch(s,p,s.size()-1,p.size()-1,flag);
        
    }
};

这是之前超时的数据
“aaaababbbaaabaabbbbabaababaabbabbaabababbaaaaaaabba”
“baaaababab****”

上面原理若看不懂不妨把数据简化手推一遍:
“aabba”
“ba**”
Good lucky!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值