題目出處
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);
}