正则表达式匹配给定集合的任何子集?

时间:2011-08-26 14:44:40

标签: regex math language-agnostic

是否可以编写一个正则表达式,它将匹配给定字符集的任何子集
a1 ... an
即它应匹配任何字符串,其中任何一个字符最多出现一次,没有其他字符,字符的相对顺序无关紧要。

立即出现的一些方法:
1. [a1,...,an]*(a1|a2|...|an)* - 这允许多个字符存在 2. (a1?a2?...an?) - 没有多重存在,但相对顺序很重要 - 这匹配任何子序列但不匹配子集
3. ($|a1|...|an|a1a2|a2a1|...|a1...an|...|an...a1),即写下所有可能的子序列(只需硬编码所有匹配的字符串:))当然,这是不可接受的。

我还猜测它在理论上可能是不可能的,因为在解析字符串时我们需要记住我们之前已经遇到过哪个字符,据我所知,正则表达式只能检查出正确的线性语言。

任何帮助将不胜感激。提前谢谢。

4 个答案:

答案 0 :(得分:4)

这不符合language-agnostic标签的要求,但是......

^(?:(?!\1)a1()|(?!\2)a2()|...|(?!\n)an())*$

<强> see a demo on ideone.com

元素第一次匹配时,后面的捕获组会“检查”它。由于该组现在已参与该匹配,因此其相应的反向引用(例如,(?!\1))的负前瞻将永远不会再次匹配,即使该组仅捕获空字符串。这是一个未记录的功能,但仍支持多种功能,包括Java,.NET,Perl,Python和Ruby。

此解决方案还需要支持前向引用(即,在组本身之前出现在正则表达式中的给定捕获组(\1)的引用)。这似乎比空团体噱头的支持要少得多。

答案 1 :(得分:3)

无法考虑如何使用单个正则表达式执行此操作,但这是使用n正则表达式执行此操作的一种方法:(我将1 2 ... {您的m s {/ p> {1}} n

a

如果以上所有内容都匹配,则您的字符串是^[23..n]*1?[23..n]*$ ^[13..n]*2?[13..n]*$ ... ^[12..m]*n?[12..m]*$ 的严格子集。

这是如何工作的:每一行都要求字符串完全

  • 从该集合中抽取的任意数量的字符,12..mn
  • 除外
  • 也许a particular one
  • 从该集合中抽取的任意数量的字符,a particular one
  • 除外

如果在每个元素依次被视为a particular one时通过,我们知道:

  • 除了允许的元素
  • 之外,字符串中没有其他内容
  • 每个允许的元素中至多有一个

根据需要。


为了完整性,我应该说,如果我接到“使用正则表达式”的命令,我只会这样做;如果没有,我会跟踪哪些允许的元素被看到,并迭代字符串的字符做明显的事情。

答案 2 :(得分:1)

不确定你是否可以获得扩展的正则表达式,但是通过简单遍历字符串很容易。

如果已经在字符串中看到任何允许的字符,则使用哈希(或数组或其他)来存储。然后,您只需遍历字符串的元素。如果你遇到一个不在你允许的集合中的元素,你就会纾困。如果它被允许,但你已经看过它,你也会拯救它。

在伪代码中:

foreach char a in {a1, ..., an}
   hit[a1] = false

foreach char c in string
   if c not in {a1, ..., an} => fail
   if hit[c] => fail
   hit[c] = true

答案 3 :(得分:0)

与Alan Moore类似,只使用\ 1,并且在看到之前没有引用捕获组:

#!/usr/bin/perl
my $re = qr/^(?:([abc])(?!.*\1))*$/;
foreach (qw(ba pabc abac a cc cba abcd abbbbc), '') {
    print "'$_' ", ($_ =~ $re) ? "matches" : "does not match", " \$re \n";
}

我们匹配任意数量的块(外部(?:)),其中每个块必须包含“恰好是我们首选集合中的一个字符,后面跟不包含该字符的字符串”。

如果字符串可能包含换行符或其他有趣的东西,可能需要使用一些标志来制作^,$和。表现如预期,但这完全取决于特定的RE风味。

只是为了愚蠢,可以使用正向前瞻断言来有效地和两个正则表达式,所以我们可以通过断言上面的匹配来测试abc的任何排列,然后检查'是N个字符长并且由这些字符组成:

my $re2 = qr/^(?=$re)[abc]{3}$/;
foreach (qw(ba pabc abac a cc abcd abbbbc abc acb bac bca cab cba), '') {
    print "'$_' ", ($_ =~ $re2) ? "matches" : "does not match", " \$re2 \n";
}