【问题标题】:grep to find words with unique lettersgrep 查找具有唯一字母的单词
【发布时间】:2013-03-04 18:34:13
【问题描述】:

如何使用 grep 从字典文件中查找出现的单词,这些单词具有给定的一组字母,并且每个字母只能出现一次。

EG 如果字母是 abc 那么预期的输出是:

出租车


编辑:

给定一个字典文件(即每行包含一个单词的文件,例如 mac os x 操作系统上的 /usr/share/dict/words)和一组(唯一)字符,我想打印出字典文件中的所有单词包含输入集的每个字符一次且仅一次。例如,如果字符集是{a,b,c},则打印出包含该集每个字符的所有(3 个字母)单词。

我正在寻找一种仅使用 grep 表达式的解决方案。

【问题讨论】:

  • 你使用什么语言?
  • bash 脚本或只是命令行。 Mac os x 电脑。

标签: grep


【解决方案1】:

给定一系列字母,例如abc,您可以将每个字母转换为前瞻,如下所示:

^(?=[^a]*a[^a]*)(?=[^b]*b[^b]*)(?=[^c]*c[^c]*)$

您可能需要使用“扩展正则表达式”标志-E 才能将此正则表达式与grep 一起使用。


要从字符串创建这个正则表达式,您可以使用sed(读者练习)

【讨论】:

  • 谢谢,看起来很有希望。但是,当我在 grep -E 或 egrep 命令行中使用该表达式时,我收到一条错误消息:“重复运算符操作数无效”。示例命令行是: grep - E 'your expression' filename
  • 您是否将正则表达式放在引号内?即grep -E 'above-regex'?
  • 是的 - grep -E 或 egrep 的 mac os x 实现似乎不包括此先行功能。当我做一个非常简单的示例时: egrep 'a(?=b)' filename 它失败并显示相同的错误消息。
  • 我在下面添加了一条评论,其中包含我找到的解决方案。对于字母 a、b、c,解决方案是:grep '^[abc]\{3\}$' /usr/share/dict/words | grep a | grep b | grep c
  • 该解决方案匹配每个字母的任何数字,而不是您的问题所问的每个字母的恰好一个“每个字母出现一次而且只有一次”。您应该将您的正则表达式从简单的a 更改为^[^a]*a[^a]*$。另外,如果您自己找到答案,您应该回答自己的问题(通过发布“答案”)并接受它(这样做完全可以)
【解决方案2】:
grep -E ^[abc]{3}.$ <Dictionary file> | grep -v -e a.*a -e b.*b -e c.*c

即找到与输入匹配的所有三个字母字符串,并通过反向 grep 将它们通过管道删除带有双字母的字符串。

我正在使用“。”在 {3} 之后,因为我的字典文件是基于 Windows 的,所以有一个额外的回车或换行。所以,这可能没有必要。

【讨论】:

    【解决方案3】:

    下面是一个 Perl 解决方案。请注意,您需要向字典中添加更多单词,并将输入读入$input 变量。一个有效单词数组将在 @results 中结束。

    #!/usr/bin/env perl
    
    use Data::Dumper;
    
    my $input = "abc";
    
    my @dictionary = qw(aaa aac aad aal aam aap aar aas aat aaw aba abc abd abf abg
      abh abm abn abo abr abs abv abw aca acc ace aci ack acl acp acs act acv ada adb
      adc add adf adh adl adn ado adp adq adr ads adt adw aea aeb aec aed aef aes aev
      afb afc afe aff afg afi afk afl afn afp aft afu afv agb agc agl agm agn ago agp
      ...
    
      PUT A REAL DICTIONARY HERE!
    
      ...
      zie zif zig zii zij zik zil zim zin zio zip zir zis zit ziu ziv zlm zlo zlx zma
      zme zmi zmu zna zoa zob zoe zog zoi zol zom zon zoo zor zos zot zou zov zoy zrn
      zsr zub zud zug zui zuk zul zum zun zuo zur zus zut zuz zva zwo zye zzz);
    
    # Generate a lookahead expression for each character in the input word
    my $regexp = join("", map { "(?=.*$_)" } split(//, $input));
    
    my @results;
    foreach my $word (@dictionary) {
    
      # If the size of the input doesn't match the dictionary word, skip to the
      # next word.
      if (length($input) != length($word)) {
        next;
      }
    
      if ($word =~ /$regexp/) {
        push(@results, $word);
      }
    }
    
    print Dumper @results;
    

    【讨论】:

    • 谢谢;我也有 shell 脚本,但我希望找到使用某种形式的 grep 的解决方案。例如 \{[abc]\} 为所有第一个字母定义了一个模式,您可以引用 \1 以使第二个字母与第一个字母相同;因此,如果能够使用 [^\1] 之类的东西来使第二个字母与第一个字母不同,那就太好了,但这似乎不起作用。
    • Perl 通常可以简化为awksedgrep 和朋友。发布您的解决方案,并询问如何将其移植到另一种语言。
    • 我找到了解决方案;对于给定的一组字母,它只使用 grep。例如,对于字母 a、b、c,解决方案是:grep '^[abc]\{3\}$' /usr/share/dict/words | grep a | grep b | grep c 对于更一般的情况,可以运行一个简单的 bash 脚本,将字母作为 $1 传递。
    【解决方案4】:

    我找到的解决方案是首先使用grep 来提取所有仅包含输入集中字母的n 字母词——尽管有些字母可能出现不止一次,但有些可能不会出现; (我再次假设输入字母是唯一的)。然后它会执行一系列 1 字母 greps 以确保每个字母至少出现一次。因为单词的长度为 n,这确保了单词包含每个字母一次且仅一次。例如,如果输入字符集是(a,b,c},那么解决方案是:

    grep -E '^[abc]{3}$' /usr/share/dict/words | grep a | grep b | grep c

    可以编写一个简单的 bash 脚本,它创建这个 grep 字符串并针对 word 文件执行它,使用 $1 作为输入字母集。它可能不是生成字符串的最有效方法,但由于我不熟悉sedawk,它似乎确实解决了我的问题。我创建的脚本是:

    #!/bin/sh
    slen=${#1}
    g2="'^[$1]{$slen}\$'"
    g3=""
    ix1=0
    while [ $ix1 -lt $slen ]
    do
      g3="$g3 | grep ${1:$ix1:1}"
      ix1=$((ix1+1))
    done
    eval grep -E $g2 /usr/share/dict/words $g3
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-18
      • 2014-05-03
      相关资源
      最近更新 更多