4

我正在尝试编写一个正则表达式,它将匹配一组字符而不考虑顺序。例如:

str = "act" 
str.scan(/Insert expression here/)

将匹配:

cat
act
tca
atc
tac
cta

但不匹配ca,accata.

我在 StackOverflow 上阅读了很多类似的问题和答案,但没有找到与我的目标完全匹配的问题。

为了澄清一点,我正在使用 ruby​​ 并且不想允许重复字符。

4

5 回答 5

5

这是您的解决方案

^(?:([act])(?!.*\1)){3}$

在 Regexr 上查看

^                  # matches the start of the string
    (?:            # open a non capturing group 
        ([act])    # The characters that are allowed and a capturing group
        (?!.*\1)   # That character is matched only if it does not occur once more, Lookahead assertion
    ){3}           # Defines the amount of characters
$

唯一特别的想法是前瞻断言,以确保字符不重复。

^并且$是匹配字符串开头和结尾的锚点。

于 2013-01-25T06:53:03.607 回答
3

[act]{3}或者^[act]{3}$会在大多数正则表达式方言中这样做。如果您可以缩小您正在使用的系统,这将帮助您获得更具体的答案。

编辑:正如@georgydyer 在下面的评论中提到的,您的问题不清楚是否允许重复字符。如果没有,您可以调整此问题的答案并获得:

^(?=[act]{3}$)(?!.*(.).*\1).*$

也就是说,一个正向前瞻来检查匹配,然后一个负前瞻与反向引用来排除重复的字符。

于 2013-01-25T00:53:52.830 回答
2

以下是我的做法:

regex = /\b(?:#{ Regexp.union(str.split('').permutation.map{ |a| a.join }).source })\b/
# => /(?:act|atc|cat|cta|tac|tca)/

%w[
  cat act tca atc tac cta
  ca ac cata
].each do |w|
  puts '"%s" %s' % [w, w[regex] ? 'matches' : "doesn't match"]
end

输出:

"cat" matches
"act" matches
"tca" matches
"atc" matches
"tac" matches
"cta" matches
"ca" doesn't match
"ac" doesn't match
"cata" doesn't match

我使用将数组传递Regexp.union给很多东西的技术;我使用散列的键特别好,并将散列传递到gsub文本模板上以进行快速搜索/替换。这是gsub文档中的示例:

'hello'.gsub(/[eo]/, 'e' => 3, 'o' => '*') #=> "h3ll*"

Regexp.union创建一个正则表达式,在提取生成的实际模式时使用它source而不是使用它很重要:to_s

puts regex.to_s
=> (?-mix:\b(?:act|atc|cat|cta|tac|tca)\b)

puts regex.source
=> \b(?:act|atc|cat|cta|tac|tca)\b

请注意如何to_s将模式的标志嵌入到字符串中。如果您不期望它们,您可能会不小心将该模式嵌入到另一个模式中,这不会像您预期的那样运行。去过那里,做到了,并有凹陷的头盔作为证据。

如果您真的想玩得开心,请查看CPAN 上可用的 Perl Regexp::Assemble模块。使用它,加上List::Permutor,我们可以生成更复杂的模式。在像这样的简单字符串上,它不会节省太多空间,但在长字符串或所需命中的大型数组上,它可以产生巨大的差异。不幸的是,Ruby 没有这样的东西,但是可以使用单词或单词数组编写一个简单的 Perl 脚本,并让它生成正则表达式并将其传回:

use List::Permutor;
use Regexp::Assemble;

my $regex_assembler = Regexp::Assemble->new;
my $perm = new List::Permutor split('', 'act');
while (my @set = $perm->next) {
    $regex_assembler->add(join('', @set));
}
print $regex_assembler->re, "\n";
(?-xism:(?:a(?:ct|tc)|c(?:at|ta)|t(?:ac|ca)))

有关在 Ruby中使用 Regexp::Assemble 的更多信息,请参阅“在 Ruby 中执行数百个文本替换的有效方法吗? ”。

于 2013-01-25T05:20:37.240 回答
1

毫无疑问 - 使用正/负前瞻和反向引用的正则表达式很巧妙,但如果你只处理三个字符,我会通过像@scones 建议的那样显式枚举字符排列来避免冗长。

"act".split('').permutation.map(&:join)
=> ["act", "atc", "cat", "cta", "tac", "tca"]

如果你真的需要一个正则表达式来扫描一个更大的字符串,你总是可以:

Regexp.union "act".split('').permutation.map(&:join)
=> /\b(act|atc|cat|cta|tac|tca)\b/

显然,如果您的搜索字符串增长,此策略不会扩展,但在我看来,这样更容易观察代码的意图。

编辑cata:根据@theTinMan 的反馈为误报添加了单词边界。

于 2013-01-25T05:18:34.053 回答
1

我将在这里假设几件事:-您正在寻找给定字符的排列-您正在使用 ruby

str = "act"
permutations = str.split(//).permutation.map{|p| p.join("")}

# and for the actual test
permutations.include?("cat")

虽然它不是正则表达式。

于 2013-01-25T01:07:47.653 回答