题目描述:
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
解题思路:
这个题目的关键就在于如何处理特殊字符 点 和 星。星号在正则中属于量词,前面必须还要跟一个非量词,点则匹配除“ ”和" "之外的任何单个字符。
因此能匹配多个的只有星,而其余字符必须一一对应。根据这个思路,我们可以根据星号把模式串分解成多个子串。注意到星号和它前面的那个字符是联系在一起的,所以我们可以把他们想象成一体,下文提到星号则语意中也包含了星号前面的字符。
第一个星号前面的字符串必须要和匹配串前面的子串一一对应,如果不行则匹配失败。如果可以匹配成功,那么我们忽略前面相同的字符串,分析后面的是否能匹配。这时新模式串的开头就是星号。
接着分析如果后面还有第二个星号,那么两个星号中间的字符串必须在新匹配串中得到匹配,否则匹配失败。在新匹配串中找到和两星中间的子串相同的子串以后,新匹配串就被匹配的子串分成了三段,第一段必须和新模式串中头部的星号匹配,并且第三段必须和新模式串中第二个星号开始的子串匹配,否则匹配失败。但是这个失败不代表整个都过程都失败,因为新匹配串中可能有多个子串能匹配两个星号中间的子串,所以我们要寻找下个匹配子串,循环检验过程,直到没有子串了才彻底失败。
如果后面没有星号,那就和检验星号前面的子串一样检验星号后面的子串,如果都匹配,那么就检验星号能不能匹配剩下的子串。
比较棘手的是如果两个星号之间的子串包含 点 号,那么不能直接用find()函数找到匹配子串。这个时候,我将第一个星号和后面的点号之间的无特殊符号子串拿出来,根据这个寻找匹配串中的子串。再以找到的子串为原点,检验两个星号之间的子串是否能全部匹配。这样可以减小搜索时间。
最后我们还要考虑到星号和点号连续出现的情况。比如第一个星号和第二个星号是连在一起的,这时我将第二个星号也纳为头部,继续在第二个星号后面寻找无特殊符号的子串,循环往复。如果第一个星号和点号之间为空串,即星号后面就是点号,那我也在点号和第二星号之间继续寻找无特殊符号的子串,最后检验的时候注意还要向前检验就行了。
下面给出我的代码:
class Solution { public: bool starMatch(const string& s, const string& p, int star) { //有'*'的匹配 string chs; for (size_t i = 0; i < star; ++i) { if (p[i*2] == '.') //里面有'.'直接返回true return true; chs.push_back(p[i*2]); } size_t i = 0, j = 0; while (i < s.size()) { if (s[i] != chs[j]) { ++j; if (j == chs.size()) return false; } else ++i; } return true; } bool isAllStar(const string& p) { if (p.empty()) return true; for (size_t i = 1; i < p.size(); i += 2) if (p[i] != '*') return false; return p.back() == '*'; } bool isMatch(string s, string p) { if (s.empty()) return isAllStar(p); size_t co = p.find_first_of('*'); //查找第一个出现'*'的位置 if (co == string::npos) { if (s.size() != p.size()) //长度不等返回假 return false; for (size_t pos = 0; pos < s.size(); ++pos) if (s[pos] != p[pos] && p[pos] != '.') //有字符不同返回假 return false; return true; //没问题返回真 } else { if (s.size() < co - 1) //s不够 return false; for (size_t pos = 0; pos < co - 1; ++pos) if (s[pos] != p[pos] && p[pos] != '.') return false; string tail_s(s, co - 1); //co-1前面的都匹配了, 检查后面的 if (tail_s.empty()) //判空 return isAllStar(p.substr(co - 1)); int numStar = 1; while (1) { //如果'*'连续出现 if (co == p.size() - 1) { //'*'后面没有字符了 string st(p, co - (2*numStar - 1), 2*numStar); return starMatch(tail_s, st, numStar); } size_t next_co = p.find_first_of('*', co + 1); //寻找第下一个'*' if (next_co == string::npos) { //没有'*',则现在这个'*'后面必须和s的尾部匹配 int i = tail_s.size()-1; for (size_t j = p.size()-1; j > co; --i, --j) if (i < 0 || (tail_s[i] != p[j] && p[j] != '.')) return false; //尾部匹配 string sub_s(tail_s.begin(), tail_s.begin() + i + 1); string st(p, co - (2*numStar - 1), 2*numStar); if (starMatch(sub_s, st, numStar)) return true; else return false; } else { //有第二个'*', 坐标为next_co string sub_p(p, co + 1, next_co - co - 2); //两个星号中间 if (sub_p.empty()) { //如果子串为空 co = next_co; ++numStar; continue; } else if (sub_p.size() > tail_s.size()) return false; size_t point_pos = sub_p.find('.'); //找到特殊符号之前的子串 string str; //找到必须匹配的部分子串,再校验其余部分 unsigned int pointNum = 0; if (point_pos == 0) { //第一个是'.' ++pointNum; while (++point_pos < sub_p.size() && sub_p[point_pos] == '.') pointNum++; if (point_pos == sub_p.size()) { //全部都是'.' string st(p, co - (2*numStar - 1), 2*numStar); for (size_t i = 0; i <= tail_s.size()-pointNum; ++i) { string sub_s(tail_s.begin(), tail_s.begin() + i); string new_s(tail_s, i + pointNum); string new_p(p, next_co - 1); if (starMatch(sub_s, st, numStar) && isMatch(new_s, new_p)) return true; } return false; } else { size_t next_point = sub_p.find_first_of('.', pointNum); if (next_point == string::npos) str = sub_p.substr(pointNum); else str.assign(sub_p, pointNum, next_point - pointNum); } } else if (point_pos == string::npos) str = sub_p; else str.assign(sub_p, 0, point_pos); size_t pos = 0; if (!str.empty()) pos = tail_s.find(str); //该子串必须在tail_s中存在匹配 while (pos != string::npos) { bool flag = false; if (point_pos != string::npos) { //如果有点则必须检验全部子串 if (pos >= pointNum) { size_t j; if (pointNum == 0) //第一个不是点 j = point_pos; else j = pointNum + str.size(); for (size_t i = pos + str.size(); j < sub_p.size(); ++i, ++j) if (i >= tail_s.size()) return false; else if (tail_s[i] != sub_p[j] && sub_p[j] != '.') { flag = true; break; } } else flag = true; } if (flag == false) { string new_p(p, next_co - 1); string new_s(tail_s, pos + sub_p.size() - pointNum); if (isMatch(new_s, new_p)) { //子串后面可以匹配 string sub_s(tail_s.begin(), tail_s.begin() + pos); string st(p, co - (2*numStar - 1), 2*numStar); if (starMatch(sub_s, st, numStar)) //子串前面也可以匹配,返回真 return true; } } pos = tail_s.find(str, pos + str.size()); //寻找s中下一个匹配的子串 } return false; } } } } };