确定正则表达式是否是另一个的子集

时间:2013-09-10 21:27:06

标签: regex regular-language halting-problem

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

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

我一开始并不确定这是否具有可判定性(它的气味就像一个不同名称的暂停问题)。但事实证明it's decidable

4 个答案:

答案 0 :(得分:12)

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的链接,应该涵盖很多常见案例。

答案 1 :(得分:5)

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

另一方面,如果正则表达式在理论意义上是“真”,只包括连接,交替和Kleene星在有限字母表的字符串上,加上这些的通常语法糖(字符类,+ ,?等,然后有一个简单的多项式时间算法。

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

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

答案 2 :(得分:4)

数学部分有一个答案: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转换和从状态m2到m2'的M2转换,那么在M12中有一个从[m1,m2]到[m1',m2'的转换]在阅读x时。
  • 最后,您将了解可达状态:
    • 如果有一对[接受,拒绝]则M2不是M1的子集
    • 如果有一对[拒绝,接受],那么M1不是M2的子集

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

答案 3 :(得分:3)

我找到了一个提供set操作的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图书馆是否(或可以作出)保证。

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