【问题标题】:Detect if a regexp is exponential检测正则表达式是否是指数的
【发布时间】:2010-07-31 09:53:29
【问题描述】:

这个article 表明在回溯时有一些正则表达式为 O(2^n)。 示例是(x+x+)+y。 当尝试匹配像 xxxx...p 这样的字符串时,它会回溯一段时间,然后才发现它无法匹配。

有没有办法检测到这样的正则表达式?

谢谢

【问题讨论】:

    标签: regex algorithm complexity-theory


    【解决方案1】:

    如果您的正则表达式引擎暴露了 (x+x+)+y 的运行时指数行为,那么它就会损坏,因为 DFA 或 NFA 可以在线性时间内识别这种模式:

    echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | egrep "(x+x+)+y"
    echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy" | egrep "(x+x+)+y"
    

    两人都立即回答。

    事实上,只有少数情况(如反向引用)真正需要回溯(主要是因为带有反向引用的正则表达式不再是语言理论意义上的正则表达式)。只有在给出这些极端情况时,一个有能力的实现才应该切换到回溯。

    平心而论,DFA 也有不利的一面,因为一些正则表达式具有指数大小要求,但大小约束比时间约束更容易执行,而且巨大的 DFA 在输入上线性运行,因此它比小回溯器被几个 X 卡住了。

    你应该真的阅读 Russ Cox 关于正则表达式的实现(以及回溯的病态行为)的优秀文章系列:http://swtch.com/~rsc/regexp/

    回答您关于可判定性的问题:您不能。因为正则表达式没有一个回溯。每个实现都有自己的策略来处理其算法在某些情况下的指数增长,并且不涵盖其他情况。一条规则可能适合这里,而那里可能是灾难性的。

    更新:

    例如,一个实现可能包含一个优化器,它可以在执行它们之前使用代数转换来简化正则表达式:(x+x+)+yxxx*y 相同,这对于任何回溯器来说都不是问题。但是同一个优化器不会识别下一个表达式,问题又出现了。这里有人描述了如何制作一个愚弄 Perl 优化器的正则表达式:

    http://perlgeek.de/blog-en/perl-tips/in-search-of-an-exponetial-regexp.html

    【讨论】:

      【解决方案2】:

      不,我不这么认为,但您可以使用以下准则:

      • 如果它包含两个在高端开放的量词并且它们是嵌套的,那么它可能是 O(2^n)。
      • 如果它不包含两个这样的量词,那么我认为它不可能是 O(2^n)。

      可能导致这种情况的量词是:*+{k,}

      还要注意,评估正则表达式的最坏情况复杂度可能与典型字符串的复杂度有很大不同,并且复杂度取决于特定的正则表达式引擎。

      【讨论】:

      • 是的,但你说“可能是 O(2^n)”有办法确定吗?有没有办法像转换正则表达式以使其显示为非指数的?
      【解决方案3】:

      任何没有反向引用的正则表达式都可以在线性时间内匹配,尽管现实世界中的许多正则表达式引擎不这样做(至少许多插入编程语言运行时环境的正则表达式引擎支持反向引用,并且不要'不存在反向引用时切换到更高效的执行模型)。

      没有简单的方法可以确定带有反向引用的正则表达式将消耗多少时间。

      【讨论】:

        【解决方案4】:

        您可以使用正则表达式解析器检测和拒绝嵌套重复,这对应于 1 的 star height。我刚刚使用 npm 的正则表达式解析器编写了 a module to compute and reject start heights of >1

        $ node safe.js '(x+x+)+y'
        false
        $ node safe.js '(beep|boop)*'
        true
        $ node safe.js '(a+){10}'
        false
        $ node safe.js '\blocation\s*:[^:\n]+\b(Oakland|San Francisco)\b'
        true
        

        【讨论】:

        • 指数正则表达式的星高为 1,但并非所有 1 正则表达式的星高都是指数的。如果你举个例子:(a|b)*a
        猜你喜欢
        • 2013-04-29
        • 1970-01-01
        • 2019-07-29
        • 2015-05-06
        • 1970-01-01
        • 2012-08-21
        • 1970-01-01
        • 1970-01-01
        • 2016-07-27
        相关资源
        最近更新 更多