【问题标题】:Determining whether a regex is a subset of another确定一个正则表达式是否是另一个正则表达式的子集
【发布时间】:2013-09-14 18:13:04
【问题描述】:

我有大量的正则表达式在匹配时调用特定的 http 处理程序。一些较旧的正则表达式无法访问(例如a.c* ⊃ abc*),我想修剪它们。

有没有给出两个正则表达式的库会告诉我第二个是否是第一个的子集?

一开始我并不确定这是否可以确定(它闻起来像是一个不同名称的停机问题)。但事实证明it's decidable

【问题讨论】:

  • 不完全确定我理解 - 你是说你有两个正则表达式 a.c*abc* 吗?你不想破译它们是否相同或部分相同?还是a.c* ⊃ abc* 是一个完整的正则表达式?因为我以前从未见过这种符号
  • ⊃ 表示严格的超集,我可能应该使用更常见的 ⊇。我想说abc* 接受的每个字符串也被a.c* 接受
  • 您对正则表达式的定义是什么?在大多数编程语言中,通常允许反向引用的正则表达式语法比正则语言更强大。所以包含的可判定性甚至不清楚......
  • 在这种情况下,我的意思是正确的正则表达式。我正在使用 RE2 库,它只实现可以直接映射到正确正则表达式的常见正则表达式功能。
  • 更正,它闻起来相当 EXPSPACE-complete ;) en.wikipedia.org/wiki/EXPSPACE

标签: regex regular-language halting-problem


【解决方案1】:

数学部分有答案:https://math.stackexchange.com/questions/283838/is-one-regular-language-subset-of-another

基本思路:

  • 计算两种语言的最小DFA
  • 计算自动 M1 和 M2 的叉积,这意味着每个状态都由一对 [m1, m2] 组成,其中 m1 来自 M1,m2 来自 M2,适用于所有可能的组合。
  • 新的转换 F12 为:F12([m1, m2], x) => [F1(m1, x), F2(m2, x)]。这意味着如果在读取 x 时 M1 从状态 m1 到 m1' 有一个转换,在读取 x 时 M2 从状态 m2 到 m2' 有一个转换,那么 M12 中有一个从 [m1, m2] 到 [m1', m2' 的转换] 在阅读 x 时。
  • 最后,您会查看可达状态:
    • 如果存在一对 [接受,拒绝],则 M2 不是 M1 的子集
    • 如果存在一对 [rejecting, accapting] 则 M1 不是 M2 的子集

如果您只计算新的转换和结果状态,从一开始就省略所有不可到达的状态,那将是有益的。

【讨论】:

    【解决方案2】:

    Trying to find the complexity of this problem lead me to this paper.

    问题的正式定义见于:这通常称为包含问题

    R, 的包含问题是测试两个给定的表达式 r, r′ ∈ R, 是否 r ⊆ r′。

    那篇论文有一些很好的信息(总结:除了最简单的表达式之外的所有表达式都相当复杂),但是搜索有关包含问题的信息会直接回到StackOverflow。该答案已经有一个指向a paper describing a passable polynomial time algorithm 的链接,它应该涵盖很多常见情况。

    【讨论】:

      【解决方案3】:

      如果正则表达式使用允许接受非正则语言的典型过程匹配器(如 Perl、Java、Python、Ruby 等中的匹配器)的“高级功能”,那么您就不走运了。这个问题通常是无法确定的。例如。一个下推自动机是否识别与另一个相同的上下文无关(CF)语言的问题是不确定的。扩展正则表达式可以描述 CF 语言。

      另一方面,如果正则表达式在理论上是“真的”,则仅由有限字母表的字符串上的串联、交替和 Kleene 星号组成,加上这些(字符类,+ , ?, etc), 那么有一个简单的多项式时间算法。

      我不能给你图书馆,但是这个:

      For each pair of regexes r and s for languages L(r) and L(s)
        Find the corresponding Deterministic Finite Automata M(r) and M(s)
          Compute the cross-product machine M(r x s) and assign accepting states
             so that it computes L(r) - L(s)
          Use a DFS or BFS of the the M(r x s) transition table to see if any
             accepting state can be reached from the start state
          If no, you can eliminate s because L(s) is a subset of L(r).
          Reassign accepting states so that M(r x s) computes L(s) - L(r)
          Repeat the steps above to see if it's possible to eliminate r
      

      将正则表达式转换为 DFA 通常使用 Thompson 构造来获得非确定性自动机。使用子集构造将其转换为 DFA。叉积机是另一种标准算法。

      这一切都是在 1960 年代完成的,现在是任何优秀的本科计算机科学理论课程的一部分。该主题的黄金标准是Hopcroft and Ullman, Automata Theory

      【讨论】:

        【解决方案4】:

        我找到了一个提供集合操作的 python 正则表达式库。

        http://github.com/ferno/greenery

        证明是Sub ⊆ Sup ⇔ Sub ∩ ¬Sup is {}。我可以用 python 库来实现这个:

        import sys
        from greenery.lego import parse
        
        subregex = parse(sys.argv[1])
        supregex = parse(sys.argv[2])
        
        s = subregex&(supregex.everythingbut())
        if s.empty():
          print("%s is a subset of %s"%(subregex,supregex))
        else:
          print("%s is not a subset of %s, it also matches %s"%(subregex,supregex,s)
        

        例子:

        subset.py abcd.* ab.*
        abcd.* is a subset of ab.*
        
        subset.py a[bcd]f* a[cde]f*
        a[bcd]f* is not a subset of a[cde]f*, it also matches abf*
        

        该库可能不可靠,因为如其他答案中所述,您需要使用最小的 DFA 才能使其正常工作。我不确定ferno's library 是否做出(或可以做出)该保证。

        顺便说一句: 使用库来计算逆或简化正则表达式非常有趣。
        a(b|.).* 简化为 a.+。这是非常小的。
        abf* 的倒数是 ([^a]|a([^b]|bf*[^f])).*|a?。试着自己想出这个!

        【讨论】:

        • 该库不能保证生成最小的 DFA,但我不认为有问题的 DFA 需要最小才能获得正确的答案。
        猜你喜欢
        • 2010-10-04
        • 1970-01-01
        • 2012-04-10
        • 2019-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-13
        相关资源
        最近更新 更多