填字游戏搜索的最佳数据结构

Posted

技术标签:

【中文标题】填字游戏搜索的最佳数据结构【英文标题】:Best data structure for crossword puzzle search 【发布时间】:2011-01-18 08:03:23 【问题描述】:

我有一个用于解决填字游戏的大型数据库,由一个单词和一个描述组成。 我的应用程序允许搜索特定长度的单词和特定位置的字符(这是很难完成的……遍历所有单词并检查每个单词)。 加上描述搜索(如有必要)

例如查找单词_ _ A _ _ B(6个字母单词,第三个字符A和最后一个B)

我想以搜索速度非常快的方式对单词进行索引。 我的第一个想法是使用平衡树结构,还有其他建议吗?

【问题讨论】:

我不知道它在执行方面是否快速,但在代码方面:我有一个谜题生成器,可以填充一个空白谜题。为此,我只是将单词存储在特定长度的 ADO 数据表中,然后创建一个视图来获取匹配项。在您的情况下,您已经拥有过滤器“__A__B”来创建视图。最好的部分是我可以将我最喜欢的代码用于我的应用程序的关键部分:其他人。 该应用程序实际上是为我父亲编写的,它是一个专门的填字游戏。他还手动检查并输入新词和描述。出于他的目的,解决方案是可以的。但我正在考虑以某种基于网络的形式获取他的数据,因此必须改进搜索部分。 【参考方案1】:

您可以将您的信息存储在某种树中(可能是三元搜索树)。 Sedgewick 和 Bentley 在this paper 的第 6 节中描述了使用 trie 进行部分搜索的算法。您当然希望对不同长度的单词进行不同的尝试。论文说,部分搜索算法需要 O(n^((k-s)/k)) 的时间,因为在 n 个 k 长度单词的 trie 中指定 s 个字母。

【讨论】:

【参考方案2】:

好的,我要提出一些奇怪的东西,但是来自C++ 我已经使用Boost 很长时间了,我是来看MultiIndex 库的。

这个库的想法是创建一个集合,但有许多不同的方式来查询它。实际上,它可以建模一个数据库。

所以,让我们把我们的话放在一个表中,并把必要的索引放在适当的位置:

word                     |length|c0|c1|c2| ... |c26|
-------------------------|------|--|--|--| ... |---|
Singapour                |9     |S |i |n | ... |0  |

现在查询将如下所示:

Select word From table Where length=9 And c2='n' And c8='u';

是不是很简单?

为获得最大效率,表应按长度分区,索引(每 cX 列一个)应在分区本地。

对于内存解决方案,每个长度都有一个容器,包含与长度一样多的索引,每个索引都是指向排序列表的哈希表(更容易合并)

这是一个python描述:

class Dictionary:
  def __init__(self, length):
    self.length = length
    self.words = set([])
    self.indexes = collections.defaultdict(set)

  def add(self, word):
    if len(word) != self.length:
      raise RuntimeException(word + ' is not ' + `self.length` + ' characters long')

    if word in self.words:
      raise RuntimeException(word + ' is already in the dictionary')

    self.words.add(word)

    for i in range(0,length):
      self.indexes[(i,word[i])].add(word)

  def search(self, list):
    """list: list of tuples (position,character)
    """
    def compare(lhs,rhs): return cmp(len(lhs),len(rhs))

    sets = [self.indexes[elem] for elem in list]
    sets.sort(compare)
    return reduce(intersection, sets)

我自愿提供了length 参数,以最小化散列的大小,从而使搜索更好。此外,集合按长度排序,以便更好地计算交集:)

如果您愿意,可以继续使用其他解决方案对其进行测试 :)

【讨论】:

【参考方案3】:

由于您使用数据库,请创建一个 Suffixes 表。 例如:

  Suffix          |   WordID   | SN
  ----------------+------------+----   
  ***           10      1
  tackOverflow            10      2
  ackOverflow             10      3
  ckOverflow              10      4
  kOverflow               10      5
  ...

使用该表,可以轻松获取在特定位置包含特定字符的所有单词, 像这样:

SELECT WordID FROM suffixes
WHERE suffix >= 't' AND suffix < 'u' AND SN = 2

2位置获取所有包含't'的单词。

更新:如果您想节省空间并牺牲一点速度,您可以使用suffix array。

您可以将所有单词存储在一行(数组)中,其中包含一个分隔符,即$,并创建 一个后缀数组,它将具有指向字符的指针。现在,给定一个字符c,您可以相当快地找到包含它的所有单词实例。不过,您必须检查它是否处于正确的位置。 (通过检查它与$s 的距离)

可能使用上述技术,搜索将比搜索原始程序中的所有单词快十倍。

更新 2: 我在我的一个实用程序中使用了数据库方法,例如,我需要定位诸如“ne”之类的后缀,但我忘记调整(优化)它这个具体问题。

你可以只存储一个字符作为后缀:

  Suffix   |   WordID   | SN
  ---------+------------+----   
  S                10      1
  t                10      2
  a                10      3
  c                10      4
  k                10      5
  ...

节省了大量空间。现在,查询变成了

SELECT WordID FROM suffixes
WHERE suffix = 't' AND SN = 2

【讨论】:

@KennyTM,是的后缀,我就是这么说的 :-) 这是否需要将所有后缀添加到数据库中?如果是这样,数据库条目的数量将会增加。 10-15 的系数。因此,搜索可能会更慢。除了数据库解决方案之外,关于如何存储和搜索此类数据的任何建议? @Drejc,是的,后缀将存储在数据库中。 IMO 的系数将是 6-7。 Suffix 应该是一个键,因此它会有一个索引(甚至更多空间:),在这种情况下搜索会很快。 O(logn) 如果我没记错的话。 @Drejc,如果您想将数据存储在内存中,并且仍然想利用查询语言的强大功能和易用性,我推荐 SQLite,它可以选择创建内存数据库。 这可能是要走的路。目前我正在查看一组大约 150.000 个单词 * 7 = 1.500.000(但这可能会上升)【参考方案4】:

这个问题:Good algorithm and data structure for looking up words with missing letters? 一开始和你问的完全一样,但后来它被编辑成更不同和更容易的东西。不过,您仍然可以在那里找到一些想法。

简而言之,大家建议将整本词典加载到内存中,并根据单词的长度将单词分组。从那里,你可以去许多不同的方向。你愿意用的内存越多,你就能跑得越快。

一个不错的建议是保留给定长度的单词列表的哈希表,这些单词在给定位置具有给定字母。你可以像这样构建它(在 Python 中):

# Build a whole lot of sorted word lists
wordlists = collections.defaultdict(list)
for word in sorted(all_words):
    for position, letter in enumerate(word):
        wordlists[len(word), position, letter].append(word)

现在,如果您需要一个以 B 结尾的 6 个字母的单词,您只需询问 wordlists[6, 5, 'B'] 即可获得完整列表。当您知道多个字母时,例如..A..B,您可以选择最短的列表,并根据所需的模式测试每个单词。我的电脑词典只有 21 个以 B 结尾的六字母单词,其中只有 SCARAB 匹配。

【讨论】:

阅读链接的问题/答案正则表达式也可能提供解决方案,以防单词提前按长度分隔。如果不进行任何测试和测量,很难判断哪种方法是正确的。【参考方案5】:

您可以使用 Suffix Tree 或 Trie。

【讨论】:

这可能是正确的方法。有关如何存储此数据以便快速搜索的任何建议。不需要数据库 - 它可以是平面文件或类似文件。 我得出的结论是,您的问题比我的小大脑可以轻松解决的问题要困难得多 :) 后缀树适用于单个字符串,所以在使用时我有点恼火它用于搜索多个单词。在单个字符串中搜索子字符串时肯定会非常快,但我不太明白如何将它应用于尼克的解决方案所做的所有单词。因此,也许我的回答是不回答;希望它有所帮助。

以上是关于填字游戏搜索的最佳数据结构的主要内容,如果未能解决你的问题,请参考以下文章

程序员的算法趣题Q66: 设计填字游戏

PROLOG中的填字游戏解算器

如何使用 GridView 或 TableView 在颤振中制作填字游戏

在 Swift 中为 iOS 设计一个填字游戏网格

在Swift中为iOS设计填字游戏网格

如何为填字游戏获得多个解决方案?