如何从包含来自用户字母输入的字典的 MySQL 数据库中获取正确的单词?
你没有。关系数据库表不是一种合适的数据结构,可以有效地解决这个问题。
你要做的是从字典中构建一个 trie 数据结构(或者,如果你真的很迷,你可以构建一个 dawg -- 一个有向无环词图 -- 这是一种压缩的 trie。)
一旦有了 trie/dawg,就可以非常便宜地针对给定机架测试字典中的 每个 单词,因为您可以“修剪”字典的整个巨大分支,而机架无法可能匹配。
让我们看一个小例子。假设您有字典“OP, OPS, OPT, OPTS, POT, POTS, SOP, SOPS, STOP, STOPS” 从中构建此树:(带有 $ 的节点是标记为“单词可以在此处结束”的节点.
^root^
/ | \
O P S
| | / \
P$ O O T
/ \ | | |
T$ S$ T$ P$ O
| | | |
S$ S$ S$ P$
|
S$
你有机架“OPS”——你是做什么的?
首先你说“我可以去 O 分支吗?”是的你可以。所以现在的问题是将“PS”与O分支匹配。你能去P支行吗?是的。它有一个词尾标记吗?是的,所以 OP 是匹配的。现在的问题是将“S”与 OP 分支匹配。你可以去T分支吗?不,你可以去S分支吗?是的。现在您有了空机架,您必须将其与 OPS 分支进行匹配。它有一个词尾标记吗?是的!所以 OPS 也匹配。现在回溯到根目录。
你能走P分支吗?是的。现在的问题是将 OS 与 P 分支相匹配。沿着 PO 分支并匹配 S - 失败。回溯到根。
再一次,你看到这是怎么回事。最后我们沿着 SOP 分支找到 SOP 的一个词尾,所以“SOP”与这个机架匹配。我们不走 ST 分支,因为我们没有 T。
我们尝试了字典中所有可能的单词,发现 OP、OPS 和 SOP 都匹配。但我们从来不用调查 OPTS、POTS、STOP 或 STOPS,因为我们没有 T。
您看到这种数据结构如何使其非常高效了吗?一旦您确定机架上没有字母作为单词的开头,您就不必调查以该开头的任何字典单词开始。如果您有 PO 但没有 T,则不必调查 POTSHERD 或 POTATO 或 POTASH 或 POTLATCH 或 POTABLE;所有那些昂贵且徒劳的搜索很快就会消失。
调整系统以处理“野生”图块非常简单;如果您有 OPS?,那么只需在 OPSA、OPSB、OPSC 上运行 26 次搜索算法......它应该足够快,以至于执行 26 次很便宜(或者如果您有两个空白,则执行 26 x 26 次。 )
这是专业拼字游戏 AI 程序使用的基本算法,当然它们还必须处理诸如棋盘位置、机架管理等问题,这使算法有些复杂。这个简单的算法版本将足够快,可以在机架上生成所有可能的单词。
不要忘记,如果字典不随时间变化,您当然只需要计算 trie/dawg 一次。从字典中构建 trie 可能很耗时,因此您可能希望这样做一次,然后想办法以适合重建的形式将 trie 存储在磁盘上快速从磁盘中。
您可以通过使用 trie 构建 DAWG 来优化内存使用。注意有很多重复,因为在英语中,很多词end是一样的,就像很多词begin一样。 trie 在开始时在共享节点方面做得很好,但在最后共享节点方面做得很糟糕。例如,您可以注意到“没有孩子的 S$”模式非常常见,并将 trie 转换为:
^root^
/ | \
O P S
| | / \
P$ O O T
/ \ | | |
T$ | T$ P$ O
| \ | | |
\ \| / P$
\ |/ |
\ | /
\ | /
\ | /
\| /
|/
|
S$
保存一大堆节点。然后你可能会注意到现在两个单词以 O-P$-S$ 结尾,两个单词以 T$-S$ 结尾,所以你可以将它进一步压缩为:
^root^
/ | \
O P S
| | / \
P$ O \ T
/ \| \ |
| | \|
| | O
| T$ |
\ | P$
\ | /
\| /
| /
|/
S$
现在我们有了这本词典的最小 DAWG。
进一步阅读:
http://dl.acm.org/citation.cfm?id=42420
http://archive.msdn.microsoft.com/dawg1
http://www.gtoal.com/wordgames/scrabble.html