搜索字谜的最快方法是啥?

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 已删除的答案包含更多细节。)

【讨论】:

以上是关于搜索字谜的最快方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Java中最快的子字符串搜索方法是啥

C++ - 在地图中搜索所有具有相同值的键的最快方法是啥?

在c ++中不同行或列旁边的矩阵中搜索最小值和最大值的最快方法是啥

最快的子串搜索算法是啥?

从给定的 IPv6 列表中搜索 IPv6:端口组合的最快搜索算法是啥:O(1)时间复杂度的端口?

在 C# 中计算数组频率分布的最快方法是啥?