zoukankan      html  css  js  c++  java
  • 10.正则表达式匹配(Regular Expression Matching)

    题目描述:

    给你一个字符串 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;
                    }
                }
            }
        }
    };
  • 相关阅读:
    vsftp 上传550 Permission denied解决办法
    FileZilla 客户端连接vsftp无法访问 Received unexpected end-of-file from SFTP server 解决之路
    vsftp的安装和使用
    linux 部署jar
    java TimeUnit 的使用
    RabbitMQ 简使用案例
    Nginx一个server配置多个location
    vue 打包部署到服务器上 配置nginx访问
    踩坑之SpringBoot WebSocker 部署Tomcat冲突
    webpack4怎么使用loader对样式资源打包
  • 原文地址:https://www.cnblogs.com/yxsrt/p/12666057.html
Copyright © 2011-2022 走看看