检查字符串是否是一堆字符的子集? (正则表达式)?

时间:2013-01-17 16:03:39

标签: regex string perl subset

我有一点问题,我有8个字符,例如“a b c d a e f g”和一个单词列表,例如: 妈妈,爸爸,坏,fag,abac

如何检查我是否可以用我的字母组成这些单词? 在我的例子中,我可以写出坏的,abac和fag,但我不能写爸爸(我没有两个D)和妈妈(我没有M或O)。

我很确定可以使用RegEx完成,但即使在Perl中使用某些函数也会有所帮助。 先谢谢你们! :)

6 个答案:

答案 0 :(得分:6)

这最简单地通过从要测试的单词形成正则表达式来完成。

这会对可用字符列表进行排序,并通过连接它们来形成字符串。然后将每个候选单词拆分为字符,排序并重新加入正则表达式.*作为分隔符。因此,例如,abac将转换为a.*a.*b.*c

然后通过测试派生正则表达式的可用字符串来确定单词的有效性。

use strict;
use warnings;

my @chars = qw/ a b c d a e f g /;
my $chars = join '', sort @chars;

for my $word (qw/ mom dad bad fag abac /) {
  my $re = join '.*', sort $word =~ /./g;
  print "$word is ", $chars =~ /$re/ ? 'valid' : 'NOT valid', "\n";
}

<强>输出

mom is NOT valid
dad is NOT valid
bad is valid
fag is valid
abac is valid

答案 1 :(得分:3)

这是为了证明可能性,而不是支持正则表达式方法。请考虑其他更合理的解决方案。

首先,您需要计算可用的字符数。

然后构建你的正则表达式(这不是Perl代码!):

从输入锚的开始开始,这匹配字符串的开头(列表中的单个单词):

^

添加尽可能多的唯一字符数:

(?!(?:[^<char>]*+<char>){<count + 1>})

示例:如果(?!(?:[^a]*+a){3})的数量为2,则a

我在这里使用了一个名为零宽度负向前瞻(?!pattern)的高级正则表达式构造。它将消耗文本,它将尽力检查字符串中的任何内容是否与指定的模式(?:[^a]*+a){3}匹配。基本上,我的想法是检查我在字符串中找不到3'a'。如果我真的找不到'a'的3个实例,则意味着该字符串只能包含2个或更少的'a'。

请注意,我使用的*+是0或更多量词,占有。这是为了避免不必要的回溯。

放置[]中可能出现的字符:

[<unique_chars_in_list>]+

示例:对于a b c d a e f g,这将变为[abcdefg]+。这部分实际上将使用该字符串,并确保该字符串仅包含列表中的字符。

以输入锚的结尾结束,它与字符串的结尾匹配:

$

因此,对于您的示例,正​​则表达式将是:

^(?!(?:[^a]*+a){3})(?!(?:[^b]*+b){2})(?!(?:[^c]*+c){2})(?!(?:[^d]*+d){2})(?!(?:[^e]*+e){2})(?!(?:[^f]*+f){2})(?!(?:[^g]*+g){2})[abcdefg]+$

您还必须为不区分大小写的匹配指定i标志。

请注意,这只考虑要匹配的单词列表中的英文字母(a-z)的情况。这里没有考虑空格和连字符。

答案 2 :(得分:1)

如何将两个字符串按字母顺序排序,然后为要检查插入的字符串排序。* 在每个字母之间如此:

'aabcdefg' =~ m/a.*b.*d.*/
True
'aabcdefg' =~ m/m.*m.*u.*/
False
'aabcdefg' =~ m/a.*d.*d.*/
False

答案 3 :(得分:0)

一些伪代码:

  • 将可用字符按字母顺序排序
  • 每个单词:

    • 将单词的字符按字母顺序排序
      • 对于单词搜索中的每个字符,通过可用字符转发以查找匹配字符。注意这个 搜索永远不会回到可用字符的开头, 匹配的字符被消耗。

甚至更好,使用字符的频率计数。 对于可用字符,构造从字符到该字符的出现次数的映射。 对每个候选单词执行相同操作并与可用映射进行比较,如果单词映射包含可用映射不匹配的字符的映射,或者映射值在单词映射中比可用映射大,则该单词不能使用可用的字符构建。

答案 4 :(得分:0)

这是一个非常简单的脚本,很容易概括:

#!/usr/bin/env perl

use strict;
use warnings;

sub check_word {
  my $word = shift;
  my %chars;
  $chars{$_}++ for @_;
  $chars{$_}-- or return for split //, $word;
  return 1;
}

print check_word( 'cab', qw/a b c/ ) ? "Good" : "Bad";

当然,如果字母列表每次都相同,那么这个函数的性能可以大大增强。实际上对于八个字符,复制哈希与每次构建一个新字符可能速度相同。

答案 5 :(得分:-2)

伪代码:

bool possible=true
string[] chars= { "a", "b", "c"}   
foreach word in words
{
     foreach char in word.chars
     {
          possible=possible && chars.contains(char)
     }
}