问题 两个正则表达式的交集


我正在寻找功能(PHP将是最好的),无论是否存在字符串匹配regexpA和regexpB,它都返回true。

例1:

$regexpA = '[0-9]+';
$regexpB = '[0-9]{2,3}';

hasRegularsIntersection($regexpA,$regexpB) 因为返回TRUE '12' 匹配两个正则表达式

例2:

$regexpA = '[0-9]+';
$regexpB = '[a-z]+';

hasRegularsIntersection($regexpA,$regexpB) 返回FALSE,因为数字永远不会与文字匹配。

感谢您提出如何解决此问题的任何建议。

亨利


6853
2018-06-03 14:29


起源

嗯......大多数正则表达式(或真正的正则表达式)等价于有限自动机。我想知道,如果同一个字母表中有两个这样的自动机,那么可以判断两种语言中是否存在某些字符串。就个人而言,我没有看到一般的方式没有暴力强迫所有长度为1,2,3,4,5的字符串......一般来说,这可能需要“永远”来检查。 - Hamish Grubijan
我很确定你必须蛮力这个。 - rfusca
@rfusca:对无限的可能输入列表进行强制攻击是非常不可判定的。如果没有交叉点,强力算法永远不会终止。 - sepp2k
@Hamish:很有可能找到两个FAs的交集(因而是两种常规语言)。唯一的问题是,在今天的编程语言中找到的大多数正则表达式引擎实际上都具有允许您匹配非常规语言的功能(反向引用是常见的)。我很确定php是其中之一。因此,如果亨利要使用有限自动机创建解决方案,那么它对所有regexen都不起作用,仅适用于那些仅使用常规功能的regexen。 - sepp2k
@ sepp2k,我很想知道如何找到两个FAs的交集并检查得到的Frankenstein是否接受任何东西。被接受的语言可以是无限的,至少FA可以用有限数量的比特来表示。 - Hamish Grubijan


答案:


对于实际上是常规的正则表达式(即不使用后向引用等不规则的功能),您可以执行以下操作:

  1. 将regexen转换为有限自动机(可以找到该算法) 这里(例如,第9章)。
  2. 构建自动机的交集(你在两个自动机状态的笛卡尔积中得到每个状态的状态。然后根据原始自动机的转换规则在状态之间转换。例如,如果你处于状态x1y2,你得到输入a,第一个自动机对输入x有一个转换x1-> x4,第二个自动机有y2-> y3,你转换到状态x4y3)。
  3. 检查新自动机中是否存在从开始状态到结束状态的路径。如果存在,则两个正则相交,否则它们不相交。

8
2018-06-03 15:23





理论。

Java库。

用法:

/**
 * @return true if the two regexes will never both match a given string
 */
public boolean isRegexOrthogonal( String regex1, String regex2 ) {
   Automaton automaton1 = new RegExp(regex1).toAutomaton();
   Automaton automaton2 = new RegExp(regex2).toAutomaton();
   return automaton1.intersection(automaton2).isEmpty();
}

3
2017-07-30 20:53





正则表达式指定可以识别可能无限的字符串集的有限状态机。字符串集可以是无限的,但状态数必须是有限的,因此您可以逐个检查状态。

以你的第二个例子为例:在第一个表达式中,要从状态0到状态1,字符串必须以数字开头。在第二个表达式中,要从状态0到状态1,字符串必须以字母开头。所以你知道在BOTH表达式中没有字符串可以让你从状态0到状态1。你有答案。

以第一个例子为例:你知道如果字符串以数字开头,你可以用正则表达式从状态0到状态1。所以现在你可以为每个消除状态0,并且只回答两个(现在是一个小的状态)有限状态机中的每一个的问题。

这看起来很像众所周知的“暂停问题”,正如你所知,对于图灵机或同等机器而言,这是一个无法解决的问题。但实际上停止问题对于有限状态机是可解的,仅仅因为存在有限数量的状态。

我相信你可以用一个非确定性的FSM来解决这个问题。如果你的正则表达式只有一个从每个状态到下一个状态的转换,那么确定性的FSM可以解决它。但正则表达式意味着,例如,如果你处于状态2,那么如果caracter是一个数字,你将进入状态3,否则如果该字符是一个字母,你将进入状态4。

所以这就是我要做的:

  1. 解决它只能从一个状态到下一个状态的一个FSM的子集。例如,匹配“Bob”和“bob”的正则表达式,以及仅匹配“bob”和“boB”的第二个正则表达式。

  2. 看看您是否可以在有限状态机中实现该解决方案。我认为这应该是可能的。状态的输入是表示一个FSM的转换和第二个FSM的转换的对。例如:状态0:如果(r1,r2)是((“B”或“b”),“b”)则状态1.状态1:如果(r1,r2)是((“o”),( “o”))然后状态2.等

  3. 现在对于更一般的情况,例如状态二返回到状态二或更早的状态;例如,正则表达式1仅识别“会面”,但正则表达式2识别具有无限数量的e的“meeeet”。你必须将它们减少到正则表达式1识别“t”和正则表达式2识别“t”。我认为这可能是由非确定性FSM解决的。

无论如何,那是我的直觉。

我不认为它是NP完全的,只是因为我的直觉告诉我你应该能够通过每个步骤缩短每个正则表达式。


2
2018-06-03 15:32



正如sepp2k指出的那样,这仅适用于真正只能识别正则表达式的正则表达式。 - Mark Lutton


有可能的。在学习语义Web技术时,我曾经使用Pellet OWL推理器遇到过它。

这是一个例子 这表明如何将正则表达式解析为树结构。然后,您可以(理论上)将您的两个正则表达式解析为树,并查看一棵树是否是另一棵树的子集,即。如果在其他树的节点中可以找到一棵树。

如果找到,那么另一个正则表达式将匹配(不仅仅是)第一个正则表达式匹配的子集。

这不是一个解决方案,但也许它会帮助你。


0
2018-06-03 15:08



这很有趣,但是......一旦你拥有了这棵树,你接下来会做什么? - Hamish Grubijan
查看两个正则表达式的语法树不会告诉您它们是否相同。两个regexen可以具有完全不同的语法树并且仍然是等价的(take /(1+|0+)+/ 和 /0(0|1)*|1(1|0)*/ 例如)。 - sepp2k
嗯,我有点困惑。怎么可以 /(1+|0+)+/ 和 /0(0|1)*|1(1|0)*/ 匹配相同的字符串?第一个正则表达式要求字符串开头 1 而另一个要求它开始 0。 (我现在有点累了,所以我可能会完全离开...) - mqchen
@ mq.chen:两个regexen都相当于 /(1|0)+/ (要么 /(0|1)+/,这是一回事)。请注意 |  - 两个regexen都可以从1或0开始。 - sepp2k