搜索字谜的最快方法是啥?
Posted
技术标签:
【中文标题】搜索字谜的最快方法是啥?【英文标题】:What is the fastest way to search for anagrams?搜索字谜的最快方法是什么? 【发布时间】:2021-09-07 06:21:35 【问题描述】:我有一个包含 267,751 个单词的 mysql 表。我尝试找到最快的方法来查找字谜,而不必为每次搜索都搜索整个表格,这将是非常低效的。
为了清楚起见:字谜是通过改变另一个单词的字母顺序而组合在一起的单词。
我想出了一个方法,我创建了一个新列,其中所有单词中的字母按字母顺序排序。在进行搜索之前,我按字母顺序对搜索词中的字母进行排序,然后在新列中进行搜索。事实证明,这种方法对于精确的字谜(具有相同字母数的单词)非常快。
问题在于找不到确切的字谜。但是要找到字谜,你可以少一个字母,少两个字母,少三个字母,一直到两个字母。突然有很多组合,平均搜索时间大约需要 0.5 秒,这很糟糕。
那里有很多字谜搜索引擎,所以这应该不难,但我想不出一个有效的方法来做到这一点。有没有人有任何想法?他们是如何做到这么快的?
谢谢
【问题讨论】:
我不确定在 MySQL 中是否有任何有效的方法。 事实上,听起来在任何语言/数据库中都很难有效地做到这一点。似乎没有任何方法可以创建优化这一点的索引。 添加了通配符,这在sql中确实没有意义。加载程序中的所有单词并搜索它们。 @TylerMiles - 正则表达式高尔夫:alf.nu/RegexGolf 闻起来像拼字游戏助手? 【参考方案1】:正如您所提到的,第一步简单而高效。
建立一个包含 2 个(或更多)列的表:
word VARCHAR(..),
sorted VARCHAR(..),
PRIMARY KEY(word),
INDEX(sorted)
sorted
具有word
的字母,但已排序。例如,使用“post”:
post -- opst
stop -- opst
pots -- opst
spot -- opst
也就是说,这会找到所有的字谜:
SELECT GROUP_CONCAT(word)
FROM anagrams
WHERE sorted = ?
当您提供排序的字母时。
对于“老鼠”:
art -- art -- Notice that the `word` == `sorted` in one case
rat -- art
tar -- art
第二步比较复杂...
通过删除一个字母将sorted
列扩展为一个简单的misspelled
列:
opst -- pst
opst -- ost
opst -- opt
opst -- ops
这是一种发现这些类型拼写错误的技术:
一个字母掉了 添加了一个字母 相邻的一对字母转置在这种情况下,你需要说
WHERE misspell IN ('opst', 'pst', 'ost', 'opt', 'ops')
当然还有INDEX(misspell)
(细节留作练习。)
第三步大致相同——IN
中的字符串越来越短。
(同样,细节留作练习。)
【讨论】:
所以每个单词会有多行,包含所有不同的拼写错误可能性?对于帖子:o、p、s、t、op、os、ot、ps、pt、st、ops、opt、ost、pst、opst?变大了;二氯二苯基三氯乙烷需要 3110399 行 @ysth - 要么很大,要么需要多次通过——每次检查一个字符较短。 (我发现这个问题中的规范很笨拙。)毕竟,该列表中的 50K 单词中可能包含“E”。 @ysth - 好吧,如果这是针对拼字游戏,那么 7 是独立单词的字数限制,而 15(?) 是通配符版本的限制。 列拼写错误有什么意义? a)如果您有一个单词 BOOK 则排序为 BKOO 无需在删除一个字母的地方添加另一列拼写错误的列 - b)此外,它会产生更多组合,因此您不能将其放入列中,并且 c)您将组合放入无论如何,排好序的列可以用于此?我错过了拼写错误列的想法【参考方案2】:不仅仅是存储一个按字母顺序排序的新列,还有一个新列,其中包含一个 like 表达式,字母按字母顺序排序,在不同字母之前和之后以及之间有一个 %,例如:
word | word_like |
---|---|
b | %b% |
bk | %b%k% |
bo | %b%o% |
boko | %b%k%o%o% |
boo | %b%o%o% |
book | %b%k%o%o% |
k | %k% |
kb | %b%k% |
ko | %k%o% |
kob | %b%k%o% |
o | %o% |
ob | %b%o% |
ok | %k%o% |
并将其用于您的搜索:
select word from words where 'bkoo' like word_like;
将“书”更改为按字母顺序排序后
【讨论】:
我认为带有前导通配符的LIKE
将无法满足“最快方式”的要求。
@RickJames 是的,但他们也说 0.5 秒太慢了。行数少于 300k,这应该会显着优于
对不起,这有什么意义或技巧?
@ysth 添加 % 而不是仅按字母顺序排列单词有什么意义?这将如何使搜索更快?
这里的重点是从给定单词中找到所有带有字母的单词;你不能只按字母顺序排序。比什么快?你还没有展示你现在在做什么;在我的慢速系统上,这比你的 0.5 秒快得多,但是 ymmv【参考方案3】:
计划 3 -- 预构建
该表将有 267K 行。每行将包含一个单词的按字母顺序排列的变体,以及包含这些字母的所有有效单词:
这种方法是一种非常快速的 PK 查找:
PK TEXT
art -- art rat tar smart artwork start ...
bkoo -- book books bookmark bookworm ...
opst -- post spot spotty fencepost ...
abbr -- barb barber abbreviate ...
这种方式使用FULLTEXT,速度相当快:
TEXT with FULLTEXT index
art rat tar smart artwork start ...
bkoo book books bookmark bookworm ...
opst post spot spotty fencepost ...
abbr barb barber abbreviate ...
既然你有词典,现在的问题是生成这个 2 列表。
如果您想匹配“x*”并且这是拼字游戏,那么板上现有的图块必须是单个字母或有效单词。示例:E_CITE -> EXCITE。利用这个原理,可以避免表格的一行需要 50K 个单词的字符串(所有单词都带有“E”)。 (不过这个算法真的很乱,FULLEXT这么高的速度值得吗?)
FULLTEXT 存在问题——停用词、短词、自动添加结尾。
【讨论】:
【参考方案4】:在琢磨通配符问题和缩短词问题后,这里有另一个部分答案:
有一个INT UNSIGNED
,其中低 26 位代表字母。没有很好的方法来使用INDEX
,但它确实提供了一种相当快速、节省空间的方法来过滤 267K 单词。
它不处理双字母(BOOK 的两个 Os)。但它会过滤“BKO”或“BKO*”。处理额外的 O 需要应用程序代码或额外的 SQL 技巧之一。
(Rob Ruchte 已删除的答案包含更多细节。)
【讨论】:
以上是关于搜索字谜的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
在c ++中不同行或列旁边的矩阵中搜索最小值和最大值的最快方法是啥