查找最长字谜的算法

Posted

技术标签:

【中文标题】查找最长字谜的算法【英文标题】:Algorithm to find longest anagram 【发布时间】:2010-12-01 23:01:12 【问题描述】:

假设我们有大约 250.000 个单词的字典。算法应该将 12 个字母作为一个数组或一个字符串,并从字典中找到匹配最长单词的变体。

当然,总是可以暴力破解它,但我想知道最优雅的方法是什么?

使用 php 以外的语言的答案如果不使用任何特定于语言的函数作为主要问题的快捷方式,也将被接受。

注意:单词存储在数据库中,但我可以将它们拉入内存以提高速度。虽然我不确定 PHP 的索引是否优于 mysql 数据库?

【问题讨论】:

你应该阅读尝试。 en.wikipedia.org/wiki/Trie 【参考方案1】:

你应该计算每个单词的签名,你只做一次,并把它和单词一起保存到你的数据库中。

表格应该是这样的:

   word varchar(12), 
   a int,
   b int, 
   c int,
    ...
   w int,
   z int;

并且从a到z的字段必须包含单词中包含的字母数, 例如,anagram 的记录如下:

word,    a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
anagram, 3,0,0,0,0,0,1,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0

一旦你有十二个字母,你必须计算集合的签名并使用它 创建一个这样的选择:

select word, length(word) as wordlen
from dictionary
where
a <= 4 and
b <= 0 and
c <= 1 and
d <= 2 and
e <= 0 and
f <= 0 and
 ....
z <= 0
order by wordlen desc;

为了拥有所有可以使用您拥有的字母集创建的单词。

没有排列,没有组合,虽然工作(编译字典)已经完成 仅一次且离线。

只是另一个提示,从数据库中删除所有超过十二个字符的单词

【讨论】:

我喜欢这个,虽然在搜索最长的单词时似乎需要扫描整个集合。我希望有一种算法可以让我使用索引。 您可以索引每个 a-z 字段。虽然这可能会占用很多空间。 您能否将使用一定数量的字符粉碎到另一列(称为“散列”)并为其编制索引。对于“字谜”,你会有一个“a3g1m1n1r1”的哈希列(或者你也可以做“aaagmnr”哈希) 如果您使用单词的签名/哈希(aaegmnr 代表经理),您将无法轻松检索子词,例如仅使用 sql 查询的 game o gamer 好的,我明白了...这就是为什么您有“... a 【参考方案2】:

我会选择对the anagram question here 的答案稍作修改的版本

对于字典中的每个单词,按字母顺序对字母进行排序。所以“foobar”变成了“abfoor”。

从您的完整输入开始,按字母顺序排序。如果没有找到,删除一个字母,重新搜索。对每个字母都这样做。然后删除两个字母……以此类推。

最坏的情况:根本找不到“字谜”。您必须测试所有可能的输入组合,这将为您提供大约 2^n 次查找,其中 n 是输入字符的数量(在您的示例中:12) 然而,算法的速度并不取决于运行时字典的大小(当然,按字母顺序排序单词)在我看来这是最重要的。

【讨论】:

可能是一个错字,但只是为了检查一下:您是 mean 'acfoor' 还是 abfoor 我实际上是从原始答案中复制了那部分,但你当然是对的【参考方案3】:

Eric Lippert 写了一篇内容丰富的blog post,关于字谜搜索。这些示例都使用 c#,但这些技术可用于任何语言。

在字典中有效搜索字谜的诀窍是要意识到所有字谜都有相同的字母,只是顺序不同。如果您“规范化”每个单词,使其字母大写并按字母顺序排列,那么检查一个单词是否是另一个单词的变位词就像比较它们的规范形式一样简单

使用这种技术,您可以轻松地从哈希表或平衡树中查找字谜。

【讨论】:

这不是和 HerdplattenToni 的回答一样吗? 当然是类似的,但是博文中包含了很多实用的调优建议,值得一提。【参考方案4】:

如果你想找到最长的匹配词,我会先尝试按词长对字典进行排序,这样你就可以把最大的精力放在最长的词上

【讨论】:

【参考方案5】:

我的想法:

伪代码:

int_32 letter_mask
int_32 permutation_match_mask
if(((letter_mask XOR permutation_match_mask) AND letter_mask)  == 0)
        YOU_HAVE_HIT;

当您在字母掩码中有非重复字母时,这很有效,但是如果您的字母(可能有)多于您可以扩展字母和排列匹配掩码

编辑

另一个想法

按字母顺序对词汇表中的单词进行排序。

如果有 12 个字母并且它们都不同,那么恰好有 4095 个可能的组合(只需 sum i= 1->12 binomial(12 over i) )(对于字母 ABCD,有 (ABCD,ABC, ABD,ACD,BCD,AB,AC,AD,BC,BD,CD,A,B,C,D) 正如我所说,12 个不同的字母中有 4095 个,如果某些字母相同,则更少。

复杂度 4095*Log2(250000) 大约是 75000。嗯,值得一试。

对每个组合进行精确搜索。

【讨论】:

想详细解释一下吗? 这是蛮力算法,需要单独检查每个单词才能找到 HIT。例如:您有字母“ABFR”和字典“FOO”中的 2 个单词,“BAR”条用“11000000000000000100000000000000”二进制表示(A 在 1,B 在 2,R 在 18)“arbf”有“11000100000000000100000000000000” “二进制当你进行上层逻辑评估时,在这种情况下只需要几条指令就可以产生 YOU_HAVE_HIT 但正如我所说的那样,它只是快速的蛮力实现。

以上是关于查找最长字谜的算法的主要内容,如果未能解决你的问题,请参考以下文章

获取所有子串(拼字游戏)的字谜的所有单词列表的算法?

算法——查找:最长连续递增子序列(部分有序)

算法的魅力—从二分查找和最长公共子序列说起

在数组中查找最长的几何级数

Java每日算法--编写一个函数来查找字符串数组中的最长公共前缀。

贪心+二分查找:最长上升子序列(3.14 leetcode每日打卡)