zoukankan      html  css  js  c++  java
  • 算法十正则表达式匹配

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

    '.' 匹配任意单个字符
    '*' 匹配零个或多个前面的那一个元素

    所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

    说明:

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

    示例 1:

    输入:
    s = "aa"
    p = "a"
    输出: false
    解释: "a" 无法匹配 "aa" 整个字符串。

    示例 2:

    输入:
    s = "aa"
    p = "a*"
    输出: true
    解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

    示例 3:

    输入:
    s = "ab"
    p = ".*"
    输出: true
    解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

    示例 4:

    输入:
    s = "aab"
    p = "c*a*b"
    输出: true
    解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

    示例 5:

    输入:
    s = "mississippi"
    p = "mis*is*p*."
    输出: false

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/regular-expression-matching
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    拿到这个问题,想了想,这个难度是困难,感觉也有点绕。分析了一下,感觉可以从头开始一个个字符串匹配,唯一要处理的是p的*和.,思路就是,如果匹配,s和p都跳到下一个,不匹配,就是出错了。

    这样需要加上*,如果当前匹配,s下一个与当前一样,p下一个是*,那么也是合适的。但是这是出现一个问题,比如s=sssa p=s*ssa,这样我无法区分*到底要匹配到哪里。

    所以想是否可以逆向匹配呢?结果分析了一下,还是没解决这个问题,比如s=sssa p=sss*a,还是没办法区分*到底匹配多少。

    这时发现,一切原因都归于在*的前后又出现了相同的字符串,那么是不是可以先把p变换一下呢把s*ssa或者sss*a都变成s*a,就是把相同的合并。

    这样写了测试代码,提交发现有问题,分析后,突然发现这里面最影响分析,最影响性能,并且把我的这个方法变成无效的规则['.' 匹配任意单个字符],这里有个组合[".*" 表示可匹配零个或多个('*')任意字符('.')],会导致回到了上面的问题到底我要匹配到哪?示例:

    s=aaabcd p=a.*abcd

    怎么办呢,看来只能在这种方式下穷举了。拿上面的举例,在匹配到.*的时候,就需要判断.*不匹配任何字符,匹配一个字符,匹配多个相同字符分别是否成功,成功,则这种匹配规则是对的。

    我的解一,看到题解中,有一个有限状态机的解法,感觉挺好的,也很好理解,然后按照那个提示写了一个所谓的穷举法。就是把匹配规则字符p按照类似的有限状态机的模式生成一个序列,每个节点定义了当前的匹配字符和是否可以重复(匹配0次或无限多次)。

    如果当前节点不是可重复的,直接判断,如果匹配直接跳到下一个节点,不匹配就直接返回false;如果当前节点是可重复的,直接跳到下一个节点,如果这条分支返回false,就匹配当前节点一次,以此循环。

    struct CHECKNODE
    {
        //当前字符
        char c;
        //是否可以循环匹配0-n次
        bool rep;
        //下一个字符节点
        CHECKNODE* pnext;
        CHECKNODE()
        {
            c = 0;
            rep = false;
            pnext = nullptr;
        }
    };
    bool checkm(const string& s, int sindex, CHECKNODE* pnow)
    {
        if (sindex == s.size())
        {
            if (pnow == nullptr)
            {
                return true;
            }
            else
            {
                if (pnow->rep)
                {
                    return checkm(s, sindex, pnow->pnext);
                }
                else
                {
                    return false;
                }
            }
        }
        if (sindex < s.size() && pnow == nullptr)
        {
            return false;
        }
        if (pnow->rep)
        {
            if (!checkm(s, sindex, pnow->pnext))
            {
                if (pnow->c == s[sindex] || pnow->c == '.')
                {
                    return checkm(s, sindex + 1, pnow);
                }
                else
                {
                    return false;
                }
                
            }
            else
            {
                return true;
            }
        }
        else
        {
            if (pnow->c == s[sindex] || pnow->c == '.')
            {
                return checkm(s, sindex + 1, pnow->pnext);
            }
            else
            {
                return false;
            }
        }
        return false;
    }
    bool isMatch(string s, string p) {
        CHECKNODE* pheard = nullptr;
        CHECKNODE* pnow = nullptr;
        for (auto& iter : p)
        {
            if (pheard == nullptr)
            {
                pheard = new CHECKNODE();
                pnow = pheard;
                pnow->c = iter;
            }
            else
            {
                if (iter != '*')
                {
                    pnow->pnext = new CHECKNODE();
                    pnow = pnow->pnext;
                    pnow->c = iter;
                }
                else
                {
                    pnow->rep = true;
                }
            }
        }
        return checkm(s, 0, pheard);
    }

     我的解法二,本以为上面创建了一个列表,内存和速度会受影响,改成了直接用引用和索引判断,结果基本没什么提升

    class Solution {
    public:
        bool checkm(const string& s, int sindex, const string& p, int pindex)
        {
            if (sindex == s.size())
            {
                if (pindex == p.size())
                {
                    return true;
                }
                else
                {
                    if (pindex + 1 < p.size() && p[pindex + 1] == '*')
                    {
                        return checkm(s, sindex, p, pindex + 2);
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            if (sindex < s.size() && pindex == p.size())
            {
                return false;
            }
            if (pindex + 1 < p.size() && p[pindex + 1] == '*')
            {
                if (!checkm(s, sindex, p, pindex + 2))
                {
                    if (p[pindex] == s[sindex] || p[pindex] == '.')
                    {
                        return checkm(s, sindex + 1, p, pindex);
                    }
                    else
                    {
                        return false;
                    }
                    
                }
                else
                {
                    return true;
                }
            }
            else
            {
                if (p[pindex] == s[sindex] || p[pindex] == '.')
                {
                    return checkm(s, sindex + 1, p, pindex + 1);
                }
                else
                {
                    return false;
                }
            }
            return false;
        }
        bool isMatch(string s, string p) {
            return checkm(s, 0, p, 0);
        }
    };

     我的解三,看到评论,可以使用动态规划,就是把一些结果保存下来,避免下次再次判断。ret[si][pi],这里面保存了,到sindex和pindex之前的匹配结果。因为在遍历中有很多需要重复判断,保存了结果就可以直接返回减少了运算。

    比如,a*可以匹配,也可以不匹配,那么.*就会判断两次是否匹配aab中的第二个a,比如a*匹配第一个a,.*匹配第二个a;或是a*不匹配,.*匹配第一个和第二个a,这样就出现了重复判断,我们可以在第一次的判断得出结果后保存下来,在第二次判断的时候直接返回。由于比较了用索引和链表的优劣势,决定还是用链表。一是好理解,二是速度更优。

        struct CHECKNODE
        {
            //当前字符
            char c;
            //是否可以循环匹配0-n次
            bool rep;
            //下一个字符节点
            CHECKNODE* pnext;
            int pindex;
            CHECKNODE()
            {
                c = 0;
                rep = false;
                pnext = nullptr;
                pindex = 0;
            }
        };
        bool checkm(const string& s, int sindex, CHECKNODE* pnow, char** aret)
        {
            bool ret = false;
            if (pnow != nullptr && aret[sindex][pnow->pindex] != -1)
            {
                ret = aret[sindex][pnow->pindex];
            }
            else
            {
                if (sindex == s.size())
                {
                    if (pnow == nullptr)
                    {
                        ret = true;
                    }
                    else
                    {
                        if (pnow->rep)
                        {
                            ret = checkm(s, sindex, pnow->pnext, aret);
                        }
                        else
                        {
                            ret = false;
                        }
                    }
                }
                else if (sindex < s.size())
                {
                    if (pnow == nullptr)
                    {
                        ret = false;
                    }
                    else
                    {
                        if (pnow->rep)
                        {
                            if (!checkm(s, sindex, pnow->pnext, aret))
                            {
                                if (pnow->c == s[sindex] || pnow->c == '.')
                                {
                                    ret = checkm(s, sindex + 1, pnow, aret);
                                }
                                else
                                {
                                    ret = false;
                                }
    
                            }
                            else
                            {
                                ret = true;
                            }
                        }
                        else
                        {
                            if (pnow->c == s[sindex] || pnow->c == '.')
                            {
                                ret = checkm(s, sindex + 1, pnow->pnext, aret);
                            }
                            else
                            {
                                ret = false;
                            }
                        }
                    }
                }
                if (pnow != nullptr)
                {
                    aret[sindex][pnow->pindex] = ret;
                }
            }
            return ret;
        }
        bool isMatch(string s, string p) {
            CHECKNODE* pheard = nullptr;
            CHECKNODE* pnow = nullptr;
            int pindex = -1;
            for (auto& iter : p)
            {
                if (pheard == nullptr)
                {
                    pindex++;
                    pheard = new CHECKNODE();
                    pnow = pheard;
                    pnow->c = iter;
                    pnow->pindex = pindex;
                }
                else
                {
                    if (iter != '*')
                    {
                        pindex++;
                        pnow->pnext = new CHECKNODE();
                        pnow = pnow->pnext;
                        pnow->c = iter;
                        pnow->pindex = pindex;
                    }
                    else
                    {
                        pnow->rep = true;
                    }
                }
            }
            char** aret = new char*[s.size() + 1];
            for (int i = 0; i < s.size() + 1; i++)
            {
                aret[i] = new char[pindex + 1];
            }
            for (int i = 0; i < s.size() + 1; i++)
            {
                for (int j = 0; j < pindex + 1; j++)
                {
                    aret[i][j] = -1;
                }
            }
            int ret = checkm(s, 0, pheard, aret);
            for (int i = 0; i < s.size() + 1; i++)
            {
                delete[]aret[i];
            }
            delete[]aret;
            return ret;
        }
  • 相关阅读:
    从跳频技术聊CDMA/WIFI之母海蒂·拉玛传奇的一生
    echarts图表X轴文字过长解决解决方案:根据文字长度自动旋转
    kafka-connect-kudu-sink插件
    mysql8远程连接问题
    zookeeper-3.4.5修改存储1M大小限制
    Java并发之CompletionService详解
    傲视Kubernetes(六):Pod管理及控制器
    傲视Kubernetes(五):注解和命名空间
    Es使用kibana增删改查以及复杂查询
    测试Ik分词器以及增加自己的词汇
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/12084395.html
Copyright © 2011-2022 走看看