题目出处
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);
}