是否存在可以从一组字符串生成正则表达式(可能仅限于简化语法)的算法,以便对与正则表达式匹配的所有可能字符串的求值重现初始字符串集?
对于具有非常“复杂”语法(包括任意重复,断言等)的正则表达式语法,找到这样的算法可能是不现实的,所以让我们从一个只允许OR
的简化算法开始。子串:
foo(a|b|cd)bar
应与fooabar
,foobbar
和foocdbar
匹配。
给定一组字符串h_q1_a
,h_q1_b
,h_q1_c
,h_p2_a
,h_p2_b
,h_p2_c
,算法的所需输出将是h_(q1|p2)_(a|b|c)
。
给定一组字符串h_q1_a
,h_q1_b
,h_p2_a
,算法的所需输出为h_(q1_(a|b)|p2_a)
。 请注意,h_(q1|p2)_(a|b)
不是正确的,因为它扩展为4个字符串,包括h_p2_b
,这不在原始字符串集中。 < / p>
我有一长串标签,这些标签都是通过将子串放在一起而生成的。我希望有一个紧凑的输出来指示列表中的标签,而不是打印庞大的字符串列表。由于完整列表是以编程方式生成的(使用一组有限的前缀和后缀),我希望紧凑符号(可能)比初始列表短得多。
((简化的)正则表达式应该尽可能短,虽然我对实际解决方案比对最好的解决方案更感兴趣。琐碎的答案当然是连接所有字符串,如A | B | C | D |。 ..但是,这没有帮助。)
答案 0 :(得分:3)
您可以尝试使用Aho-Corasick算法从输入字符串创建有限状态机,之后生成简化的正则表达式应该有点容易。您的输入字符串为例:
h_q1_a
h_q1_b
h_q1_c
h_p2_a
h_p2_b
h_p2_c
将生成一个有限的机器,最可能看起来像这样:
[h_] <-level 0
/ \
[q1] [p2] <-level 1
\ /
[_] <-level 2
/\ \
/ \ \
a b c <-level 3
现在,对于特里的每个级别/深度,所有的叮咬(如果是多个)都将放在OR
括号下,所以
h_(q1|p2)_(a|b|c)
L0 L1 L2 L3
答案 1 :(得分:3)
如果您想要找到的是一组字符串的最小有限状态机(FSM),则可以直接解决此问题。由于生成的FSM不能有循环(否则它将匹配无限数量的字符串),因此应该很容易使用连接和析取(|
)运算符转换为正则表达式。虽然这可能不是最短的正则表达式,但如果您使用的正则表达式库编译为最小化的DFA,它将导致最小的编译正则表达式。 (或者,您可以直接将DFA与Ragel这样的库一起使用。)
如果您可以访问标准FSM算法,那么过程很简单:
通过将每个字符串添加为状态序列来创建非确定性有限状态自动机(NFA),每个序列从起始状态开始。显然是O(N)
字符串的总大小,因为原始字符串中的每个字符都只有一个NFA状态。
从NFA构造确定性有限状态自动机(DFA)。 NFA是一棵树,甚至不是DAG,它应该避免标准算法的指数最坏情况。实际上,您只是在这里构建一个前缀树,您可以跳过第1步并直接构造前缀树,将其直接转换为DFA。前缀树的节点数不能超过原始字符数(如果所有字符串都以不同的字符开头,则节点数可以相同),因此其输出大小为O(N)
,但我没有在我的头顶上证明它也是O(N)
及时。
尽量减少DFA。
DFA最小化是一个经过充分研究的问题。 Hopcroft algorithm是最差情况O(NS log N)
算法,其中N
是DFA中的状态数,S
是字母表的大小。通常,S
将被视为常数;无论如何,Hopcroft算法的预期时间要好得多。
对于非循环DFA,有线性时间算法;最常被引用的一个是由于Dominique Revuz,我找到了它的粗略描述here in English;原始论文似乎是付费的,但Revuz's thesis (in French)可用。