缩写是一串字母数字字符。数字代表要跳过的字母数,例如 i18n 是国际化的缩写。inter15 和 20 也是如此。假设您有一本单词词典,在词典中找到与给定缩写匹配的所有单词的最快方法是什么?你可以为你的字典使用你喜欢的任何数据结构,但是查找匹配单词的算法必须比 O(n) 更好,其中 n 是字典中的单词数。
3 回答
所以你有一个查询是prefix - count - suffix
. 有几种方法可以解决这个问题。
如果前缀永远不会为空,那么您可以构建一个前缀树(它只是一个 trie),并查询以该前缀开头的所有单词,过滤那些具有请求长度和后缀的单词。
您可以通过构建一个通用的后缀树来做同样的事情。
或者,由于前缀或后缀可能为空,您可以构建前缀树和后缀树。查询两者,过滤长度,然后合并结果。
您可以想象构建一个单一的前缀-后缀树。数据结构比拥有两棵独立的树要复杂一些,但它需要的内存更少。
我记得(已经有几年了),您可以使用有向无环词图 (DAWG) 完成所有这些以及更多操作(搜索缺少字母的词等)。
这是 a10n 树的一个想法(缩写树,或者应该是 a10n t2e?)。
丢失的字母被丢失的块的长度替换,所以匹配的长度是事先知道的。将字典细分为包含恒定长度单词的子字典是有意义的:
dict -> dict2 -> {ad, ah, am, an, as, at, ...}
-> dict3 -> {abe, abo, abu, ace, act, ada, add, ... }
-> dict4 -> {abba, abbe, abby, ...}
-> ...
每个字典都包含一个有序的单词列表。如果缩写是“5”,则返回长度为 5 的子字典的列表。如果缩写是“green”,即根本没有缩写,则使用二进制搜索检查该单词是否在列表中。
处理了琐碎的案例,我们必须找到一种快速搜索此列表的方法。让我们介绍一个位置和字母树。树的第一层指的是字母的位置,第二层指的是字母本身,就像在 trie 中一样,例如:
dict3 -> i == 0 -> a -> {ant}
-> b -> {bat, bee}
-> c -> {cat, cod, cow}
-> ...
i == 1 -> a -> {bat, cat, rat}
-> b -> {}
-> c -> {}
-> ...
现在找到所有给定字母的列表的交集。如果我们正在寻找“c2”,我们会在 (0, c) 处获取列表。如果我们正在寻找“b1t”,我们在 (0, b) 和 (2, t) 处取列表的交集。当列表被排序时,找到交叉点应该相当快。
还有其他方法。Tries 是一种数据结构,可以让我们快速搜索字典。我在对现已删除的帖子的评论中说过,在这种情况下,我认为尝试不是一种合适的数据结构,因为尝试在第一个字母已知的情况下有助于查找单词。但我现在不太确定:当找到通配符(即丢失的字母)时,可以遍历 trie 的所有分支。对于像 'i18n' 这样的词,分支的数量似乎很大,但实际上,会有很多空分支。在我大约 45,000 个单词的(小)测试字典中,没有匹配“i18n”的单词,甚至没有国际化。因此,对于真正的字典,尝试甚至可能是一种选择。
(向删除答案的用户道歉,但我在评论中提到了字典的单个尝试。如果字典不庞大,每个长度的子尝试都可能有效。)
一种解决方案是对多个数组中的所有单词进行分类。每个数组都包含具有特定数量字母的所有单词。例如,一个包含 4 个字母的单词的数组,例如:food、wood、like、pike 等,另一个包含 5 个字母的单词等。
因此,使用您的缩写 i18n,您拆分字符和数字:“in”“18”并将字符数量添加到数字:2+18 =20。现在您知道您的单词在包含 20 个字母的单词数组中。
我认为我们可以做得更好,但这比在整个字典中寻找一个单词要好。