leetcode模式匹配算法

題目出處

  https://leetcode.com/problems/regular-expression-matching/

  實現簡單的正則表達式算法,正則表達式中支持".", "*"兩個特殊字符, 並實現整個字符串匹配。

  ".": 表示任意一個字符。

 "*": 表示任意多個個前一個字符。

例: 

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

分析

注意到根據長度可以把正則表達式分爲兩種形式:

   固定長度的子字符串:

如"c*a*b" 表達式中就是 “b" 長度固定爲1.

子字符串中也包含特殊字符"."

   可變長度的模式子字符串: 組成的都是長度不固定的子字符串。

如"c*a*b" 表達式中就是 “c*a*" 長度不固定,並且長度可以是0.

子字符串的形式爲: 多個{a}*相邊, {a}變量可以是任意字符,也可以是特殊字符", "。 

注意到如果索引從1開始的話,子串的偶數位必須是特殊字符"*"


把表達式按這個規則分成子串列表,則必然是上面兩種形式的子串交替進行。可遞歸實現此算法。


算法:

1. 分解輸入的正則表達式,獲得子模式列表。

2. 如果是可變模式時,嘗試逐個判斷, 進行遞歸。具體可看代碼實現


3. 如果是固定子模式時,位置可直接判斷


代碼實現

爲了便於閱讀,分解了幾個功能函數來處理。

1.分解表達式

vector<string> parsePattern(string p){
        vector<string> p_vector;
        int start = 0;
        for(int i = 1; i < p.length(); i++){

            if(p[i] != '*')continue;
            //添加固定項
            if(i - start >1)
                p_vector.push_back(p.substr(start, i-1-start));
            //查找下一個兩個的數字 尋找a*b*c*模式的結束點
            int j = i+2;
            while(j < p.length() && p[j]== '*') j += 2;
            p_vector.push_back(p.substr(i-1, j-i)); //加入子模糊模式 a*b*c*
            start = i = j-1;
        }
        if(start < p.length())
            p_vector.push_back(p.substr(start));
        return p_vector;
    }
2. 檢查一個固定模式是否匹配

//檢查固定的數據
    bool check(string &s, int start, int end, string p){
       if(end-start != p.length()) return false;
       for(int i = 0; i < p.length(); i++)
            if(p[i] != '.' && p[i] != s[start+i]) return false;
       return true;
    }

3. 檢查可變模式是否匹配
// 檢查長度不固定的模式
    bool checkMode(string &s, int start, int end, string p){
        if(start == end || p.find(".*") != string::npos) return true;
        for(int i = 0; i < p.length(); i+= 2){
            while(start < end && s[start] == p[i]) start++;
            if(start >= end) return true;
        }
        return (start >= end);
    }

4. 處理子模式列表中的第pi個模式匹配

bool checkVector(string &s, int start, int end, vector<string> &p, int pi){ 
        
        if(p.size() <= pi)return start == end; 
        
        if(p[pi].length() > 1 && p[pi][1] == '*'){
            
            //爲了下速處理,對可變模式當前位於最後,或倒數第二位時作特殊處理
            //就有了下面的兩個if語句,節省代碼可直接去掉,,不影響結果。
            if(pi+2 == p.size()){
                if(start + p[pi+1].length() > end) return false;
                if(!check(s, end-p[pi+1].length(), end, p[pi+1]))return false;
                end -= p[pi+1].length();
            }
            if(pi+2 >= p.size()) return checkMode(s, start, end, p[pi]);
            //由於未處理的模式長度大於2時纔會走到這裏,所以p[pi+1].length() 就不需要判斷是否超界
            //如果前面沒作優化,則需要判斷
            for(int i = start; i + p[pi+1].length() <= end; i++){
                if(!checkMode(s, start, i, p[pi])) return false;
                if(checkVector(s, i, end, p, pi+1))return true;
            }
            return false;
        } 
        if(end - start < p[pi].length()) return false;
        if(!check(s, start, start + p[pi].length(), p[pi]))return false;
        start += p[pi].length(); 
        return checkVector(s, start, end, p, pi+1);

    }

5. 對外接口函數

bool isMatch(string s, string p){
        if(s == p) return true;
        if(p == "" && s != "") return false; 
        //分解格式
        vector<string> p_vector = parsePattern(p); 
        return checkVector(s, 0, s.length(), p_vector, 0);
    }



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