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!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章