10. Regular Expression Matching

題目

Implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)

Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

思路

分為三種情況:

  • 單獨的“.”:直接跳過一個字符
  • 單獨的“*”:依次去掉若干相同的字符,判斷后面是否匹配
  • “.*”一起:依次去掉若干字符,判斷后面是否匹配

在后兩種情況下可以采用遞歸簡化代碼。

實現一

class Solution {
public:
    bool isMatch(string s, string p) {
        int i=0, j=0;
        while(i<p.size()){
            if(i+1<p.size() && p[i+1]=='*'){
                if(p[i]=='.'){
                    for(int m=j; m<=s.size(); m++){
                        if(isMatch(s.substr(m,s.size()-m),p.substr(i+2,p.size()-i-2)))
                            return true;
                    }
                }
                else{
                    for(int m=j; m<=s.size() && ((m==j)||(s[m-1]==p[i])); m++){
                        if(isMatch(s.substr(m,s.size()-m),p.substr(i+2,p.size()-i-2)))
                            return true;
                    }
                }
                return false;
            }
            else if(p[i]=='.'){
                i++; j++;
            }
            else{
                if(s[j]==p[i]){
                    i++; j++;
                }
                else{
                    return false;
                }
            }
        }
        return i==p.size()&&j==s.size();
    }
};

思考一

為了快速實現功能,代碼寫得巨丑無比。而且遞歸真的很慢,估計時間復雜度可能達到指數水平,運行時間排在后三分之一左右。粗略看了排名靠前的代碼,使用了動態規劃。所以現在想想怎么把遞歸轉換成動態規劃。可以達到O(nm)的時間復雜度。

  • 首先考慮將遞歸轉化為記憶化搜索。用二維數組d[a][b]表示s.substr(a,s.size()-a)與p.substr(b,p.size()-b)是否匹配。這就避免了遞歸。
  • 再考慮將記憶化搜索轉化成動態規劃,關鍵在于推導其遞推關系。
    數組要使用的大小為dp[s.size()+1][p.size()+1]。
    考慮初始條件。一開始打算使用dp[s.size()][p.size()]代表兩個空串的關系,自然是相等的,為true。另外當p的子串中沒有“*”時,長度不等的子串必然不等,所以數組的大部分空間內必然都是false。所以可以考慮現將數組初始化為false,再將dp[s.size()][p.size()]置為true。但實際操作中發現這樣從后往前會帶來一些問題,所以后來使用dp[0][0]代表兩個空串的關系,置為true。中間的遞推公式搞了半天,不過看著答案邊想邊寫總算弄出來了。

實現二

class Solution {
public:
    bool isMatch(string s, string p) {
        vector<vector<bool>> dp(s.size()+1, vector<bool>(p.size()+1, false));
        dp[0][0] = true;
        for(int i=0; i<=s.size(); i++){
            for(int j=1; j<=p.size(); j++){
                if(p[j-1]=='*'){
                    dp[i][j] = dp[i][j-2] || (i>0 && (s[i-1]==p[j-2] || p[j-2]=='.') && dp[i-1][j]);
                }
                else{
                    dp[i][j] = i>0 && dp[i-1][j-1] && (p[j-1]=='.' || s[i-1]==p[j-1]);
                }
            }
        }
        return dp[s.size()][p.size()];
    }
};

思考二

用上面的方法達到了前三分之一的水平。再看題解,發現還有O(n)的解法,大驚失色。
看了下,和我之前寫的方法一差不多,感覺這題解的時間復雜度估計有問題。所以懶得寫了,復制下來直接提交,結果發現在后40%左右,和我一開始想出來的方法差不多。看來遞歸還是慢啊……

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容