【问题标题】:Is there a more efficient way to do scrabble search with grep?有没有更有效的方法来使用 grep 进行拼字游戏搜索?
【发布时间】:2015-06-14 03:37:42
【问题描述】:

我正在解决unix中的以下问题

  1. 假设您正在玩拼字游戏。你的架子上有以下七个字母——E A F N A S M。这些是你可以用来组成单词的字母,你可以在单词中使用任意数量的字母,但必须至少使用一个。您正试图在黑板上已经有一个单词的位置放置一个单词:ARE。

    您的目标是找到一个单词,该单词将与您在机架中的字母一起附加到单词 ARE。虽然通常你的字母可以放在 ARE 之前或之后来组成一个新单词,但在这种情况下,ARE 位于棋盘的边缘,所以你的单词必须以 ARE 结尾。您的目标是使用 grep 在 /usr/dict/words 中找到所有符合这些条件的可能单词。

我想出的命令确实效率低下但有效。

grep “^[eafnasm][eafnasm]*are$” /usr/dict/words |
grep -v “a.*a.*a.*a” |
grep -v “e.*e.*e” |
grep -v “f.*f” |
grep -v “n.*n” |
grep -v “s.*s” |
grep -v “m.*m” |
grep -v “^...........”

有没有更有效的方法?

【问题讨论】:

  • 不要在 shell 脚本中使用花引号。
  • 每个字符类中的第二个a 是多余的。您可以改为将第一个正则表达式写为^[eafnsm]\+are$
  • 您确定必须继续 ARE 这个词还是必须越过它?
  • @CasimiretHippolyte:上面写着“你的词必须以 ARE 结尾”。

标签: regex unix grep command words


【解决方案1】:

加快速度的一种方法是:

grep -E '^[aefmns]{1,7}are$' /usr/dict/words |
grep -Ev 'a.*a.*a.*a|e.*e.*e|f.*f|n.*n|s.*s|m.*m'

它减少了查看数据的进程数量。我从初始字符类中删除了第二个 A,因为它是多余的,但重复表示的成本可以忽略不计。在第一个模式中使用 {1,7} 限定符意味着不需要在第二个模式中过滤过长的名称。

请注意,第一次搜索不允许多个 R 通过。它是专门针对手上字母和板上文字的这种特定组合的。如果手拿着一个 R(而不是第二个 A),那么有必要从结果中过滤掉 2 个以上的 R(两个,因为在这种情况下,手上有一个 R,单词中有一个板上),并且多 A 过滤器也必须更改。

请注意,此处的更改只是对正在运行的原始 8 个grep 命令的细微调整。由于该解决方案需要使用 grep(排除 Perl、Python、Awk 等),因此您可能无法使用少于两个命令,一个“肯定”grep 用于选择可能性,一个“否定'grep 以消除不可能。使用自定义工具(用 C 或 C++ 或类似工具编写的专用程序),您可能会做得更好。

如果您的grep 版本支持PCRE(与Perl 兼容的正则表达式),您也许可以“合二为一”。我相当肯定它的可读性和可理解性会降低,虽然它的性能可能会好一些(因为没有管道,所以 I/O 会更少),但必须测量性能改进。有时,越简单越好。

【讨论】:

  • 我更喜欢grep -Ev '(a.*a.*a|e.*e|f.*f|m.*m|n.*n|s.*s).*are$',因为它更清楚每个字母有多少是太多。
  • fanfare这个词怎么样?
  • @AdamSpiers: 是的,但是通过这种方式,字符串需要测试到最后。
  • @l'L'l -- Tarantara... FANFARE 这个词怎么样?它包含 2 个字母 F,因此被第二个 grep 排除在外——否定的 grep
  • @l'L'l:关于你的正则表达式; SEMISPHERE 应该被两者允许通过,因为它只包含 2 个 S,但第一个正则表达式消除了带有 4 个 S 的行(单词),第二个消除了带有 SSS 的行,然后是任何东西和另一个 SSS。 -v 在工作中使用了扳手;它否定了逻辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-21
  • 1970-01-01
  • 2019-08-20
  • 2023-04-03
  • 2014-02-22
相关资源
最近更新 更多