10.正则表达式匹配(Regular Expression Matching)

题目描述:

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。

解题思路:

  这个题目的关键就在于如何处理特殊字符 点 和 星。星号在正则中属于量词,前面必须还要跟一个非量词,点则匹配除“\n”和"\r"之外的任何单个字符。

  因此能匹配多个的只有星,而其余字符必须一一对应。根据这个思路,我们可以根据星号把模式串分解成多个子串。注意到星号和它前面的那个字符是联系在一起的,所以我们可以把他们想象成一体,下文提到星号则语意中也包含了星号前面的字符。

  第一个星号前面的字符串必须要和匹配串前面的子串一一对应,如果不行则匹配失败。如果可以匹配成功,那么我们忽略前面相同的字符串,分析后面的是否能匹配。这时新模式串的开头就是星号。

  接着分析如果后面还有第二个星号,那么两个星号中间的字符串必须在新匹配串中得到匹配,否则匹配失败。在新匹配串中找到和两星中间的子串相同的子串以后,新匹配串就被匹配的子串分成了三段,第一段必须和新模式串中头部的星号匹配,并且第三段必须和新模式串中第二个星号开始的子串匹配,否则匹配失败。但是这个失败不代表整个都过程都失败,因为新匹配串中可能有多个子串能匹配两个星号中间的子串,所以我们要寻找下个匹配子串,循环检验过程,直到没有子串了才彻底失败。

  如果后面没有星号,那就和检验星号前面的子串一样检验星号后面的子串,如果都匹配,那么就检验星号能不能匹配剩下的子串。

  比较棘手的是如果两个星号之间的子串包含 点 号,那么不能直接用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;
                }
            }
        }
    }
};

 

posted @ 2020-04-09 12:20  简讯  阅读(320)  评论(0编辑  收藏  举报