【问题标题】:Algorithm for matching partially filled words匹配部分填充词​​的算法
【发布时间】:2011-02-03 15:42:39
【问题描述】:

我正在编写一个游戏,当给定一个部分填充的单词时,它会搜索字典并返回所有匹配的单词。为此,我试图找到一种可用于上述目的的算法。例如,给定 - - a -,算法将在字典中搜索所有长度为 4 且第三个字母为 'a' 的单词。

已经有这样的算法了吗?如果没有,有人可以大致了解如何设计这样的算法吗?

提前致谢。

【问题讨论】:

  • 这取决于您对字典的访问权限。您是自己制作索引(或多个索引),还是使用外部搜索引擎?这将对解决方案产生重大影响!
  • @Etamar,对不起,我错过了解释那部分。我正在寻找的算法实际上将索引字典(由我构建)并允许搜索和匹配部分填充的单词)

标签: algorithm


【解决方案1】:

嗯,它还不存在,但已经在 SO 上进行过研究,用于填字游戏问题。

我提出的解决方案的要点是按字母和索引进行索引,这是 Python 提供的:

class Index:
  def __init__(self, list):
    self.data = defaultdict(set)
    for word in list: self.add(word)

  def add(self, word):
    for l in range(0, len(word)):
      self.data[(l, word[l])].insert(word)

  def look(self, letters):
    """letters is a list of tuples (position, letter)"""

    result = None
    for (p,l) in letters:
      set = self.data[(p,l)]
      if result == None: result = set
      else: result = result.insersection(set)

    return result

这个想法很简单:你有一个很大的索引,其中包含每对 (position,letter) 的一组单词。在您的情况下,它可以扩展为每个字长有一个索引,这会缩小字集的大小,从而更快。

对于检索,您只需将所有集合相交以获得与所有已知字母匹配的公共单词集合。

【讨论】:

  • 相交两个结果集在理论上很好,您使用的语言实际上可能很容易做到,但这并不意味着它有效。除非结果集被表示为一些可以进行与运算的位掩码,否则将它们相交将是一个线性操作:您查看集合 1 中的每个项目,看看它是否也在集合 2 中找到,如果它被移动到结果集。那太没用了!既然您正在查看第一个结果集中的每一个结果,为什么不直接测试它是否符合所需的掩码并跳过第二个结果集?
  • 不错的主意——易于实施,而且速度可能足够快。在几乎没有限制的情况下,它比只缺少一个字母的所有单词更有效地找到很多单词。
  • Python 在集合方面做得很好 :) 首先,集合是使用散列实现的,所以检索是O(1)。其次,如果您需要性能,您可以迭代较少的集合。为简单起见,我在这里合并,但在我提到的关于拼字游戏的帖子中,我列出了所有集合,然后按大小对它们进行排序,并在较小的集合上迭代合并它们(越来越小),这当然更有效。这是原始算法:stackoverflow.com/questions/2288901/…
【解决方案2】:

另一种解决方案是将您的字典构造为prefix tree。然后你的算法将只需要穿过那棵树。对于每个节点,您都知道与哪个字母相关联以及在单词中的位置,因此您知道它是否与您要查找的字母匹配。如果没有,你就停下来,不要通过它的孩子。您还知道何时超出查询的长度。您到达的每一片叶子都可以添加到结果列表中。

就内存消耗而言,此解决方案可能非常有效。

【讨论】:

    【解决方案3】:
    test = '--a-';
    
    for each (words as word)
    {
        if ((word.length == test.length)
            && (test.index(0) == '-' || (word.index(0) == test.index(0)))
            && (test.index(1) == '-' || (word.index(1) == test.index(1)))
            && (test.index(2) == '-' || (word.index(2) == test.index(2)))
            && (test.index(3) == '-' || (word.index(3) == test.index(3))))
        {
            // match
        }
    }
    

    这是你需要的吗?显然它需要稍作修改才能适用于不同的长度。

    【讨论】:

    • @Coronatus,以这种方式检查效率低下。将要使用的字典很大。
    • 并非如此。首先,它将单词过滤为正确长度的单词(如果长度不同,则不检查其他条件)。此外,我看不出算法可以如何以不同的方式完成。
    【解决方案4】:

    据我了解,您不能使用正则表达式查询吗?在上面的示例中,模式类似于??a?

    然后你需要遍历所有单词并检查是否匹配。

    【讨论】:

    • 确实这可能行得通。虽然你需要比? 更好的东西,可能是[a-zA-Z],因为你不想匹配任何东西,只是一个字母。剩下的唯一问题是操作的速度。
    【解决方案5】:

    如果您在一台功能相当强大的计算机上运行(与负载相比),那么 PierrOz 有一个很好的答案:将字典存储为前缀树。然后,您可以进行广度优先搜索,仅当您达到您真正知道该字母的水平时才进行修剪。

    如果您需要更快的解决方案,您需要一种限制搜索深度的方法。一种可能性是对答案进行分类。例如,您可以先按长度对单词进行分组;那么你只需要浏览一定长度的单词列表。然后您可以按包含特定字母的单词进行分组——所有字母对可能就足够了。这为您提供了一个包含 13000 个元素的数组,您可以快速索引到这些元素:计算单词中的字母数量,然后选择单词中最稀有的一两个字母,并使用它来索引一个仅用这些字母保存该长度的单词。在大多数情况下,这种策略应该可以让每个 bin 减少几百个单词,并且即使您选择了树的大部分宽度,前缀树搜索也应该很快。

    【讨论】:

      猜你喜欢
      • 2012-07-07
      • 2013-10-28
      • 2016-10-10
      • 1970-01-01
      • 2013-01-20
      • 1970-01-01
      • 2019-06-14
      • 2013-08-08
      • 2013-06-04
      相关资源
      最近更新 更多