从字典中获取字谜列表

Posted

技术标签:

【中文标题】从字典中获取字谜列表【英文标题】:get list of anagrams from a dictionary 【发布时间】:2012-06-21 22:32:47 【问题描述】:

基本上,字谜就像字符串的排列。例如 stacksacktstakc 都是stack 的字谜(认为上面的词没有意义)。不管怎样,你本可以理解我的基本意思。

现在,我想要一个anagrams 的列表,给定百万个单词,或者简单地从字典中说出来。

我的基本问题是Find total number of unique anagrams in a dictionary?

排序和比较 不会工作,因为它的时间复杂度非常糟糕。

我想到了使用哈希表,字符串作为键。

但问题是散列函数应该是什么?如果有一些伪代码会很有帮助 假如。其他一些比上述方法更好的方法也会有所帮助。

谢谢。

【问题讨论】:

问题不太清楚。你能改述一下目标吗? 您的意思是:我有一本包含一百万个单词的字典,我希望识别字典中所有相互变位的单词集?例如。如果字典包含:[tap, pat, pot, top] 您希望看到 [[tap, pat], [pot, top]]? 是的@Alex。我只想知道有多少不同的字谜? @NicholasDiPiazza 希望你,我的目标很清楚。 排序是这里的解决方案,如果你假设字长有一些恒定的上限,它的复杂性是线性的。你只需要排序正确的东西;字符,而不是单词。 【参考方案1】:

显而易见的解决方案是将每个字符映射到一个素数并乘以这些素数。所以如果 'a'' -> 2 和 'b' -> 3,那么

'ab' -> 6 'ba' -> 6 'bab' -> 18 '阿巴' -> 36 'baba' -> 36

为了最小化溢出的机会,最小的素数可以分配给更频繁的字母(e,t,i,a,n)。注意:第 26 个素数是 101。

更新: an implementation can be found here

【讨论】:

你仍然需要处理溢出,这可能会导致“冲突”。可能是通过存储每个条目的字母频率直方图。 嗯:谢谢!请注意(一旦你必须处理冲突)它也适用于非质数(random)数字。这类似于 Zobrist 散列。但使用素数时,它看起来更干净。【参考方案2】:

一个可能的散列函数可以是(假设只有英文单词)每个字母出现次数的排序计数。因此,对于“字谜”,您将生成 [('a', 3), ('g', 1), ('n', 1), ('m', 1), ('r',1)]。

或者,您可以通过从您的单词生成位掩码来获得不精确的分组,其中对于位 0-25,每个位表示该字母的存在或不存在(位 0 表示“a”到位 25 表示“z”)。但是随后您必须进行更多处理以进一步拆分每个散列组以区分例如“to”从“too”。

这些想法有帮助吗?任何特定的实现语言(我可以使用 C++、python 或 Scala)?

编辑:添加了一些示例 Scala 代码和输出:

好的:我现在处于 Scala 模式,所以我已经敲定了一些东西来做你所要求的,但是(咳咳)如果你对 Scala 或函数式编程不太熟悉,可能不太清楚.

从这里使用大量英语单词:http://scrapmaker.com/data/wordlists/twelve-dicts/2of12.txt

我在它们上运行这个 Scala 代码(在脚本模式下使用 Scala 2.9 大约需要 5 秒,包括编译时间,字典大约 40,000 个单词。不是最有效的代码,但首先想到的) .

// Hashing function to go from a word to a sorted list of letter counts
def toHash(b:String) = b.groupBy(x=>x).map(v => (v._1, v._2.size) ).toList.sortWith(_._1 < _._1)


// Read all words from file, one word per line
val lines = scala.io.Source.fromFile("2of12.txt").getLines

// Go from list of words to list of (hashed word, word)
val hashed = lines.map( l => (toHash(l), l) ).toList

// Group all the words by hash (hence group all anagrams together)
val grouped = hashed.groupBy( x => x._1 ).map( els => (els._1, els._2.map(_._2)) )

// Sort the resultant anagram sets so the largest come first
val sorted = grouped.toList.sortWith( _._2.size > _._2.size )

for ( set <- sorted.slice(0, 10) )

    println( set._2 )

这会导出前 10 组字谜(首先是成员最多的一组):

List(caret, cater, crate, react, trace)
List(reins, resin, rinse, risen, siren)
List(luster, result, rustle, sutler, ulster)
List(astir, sitar, stair, stria, tarsi)
List(latrine, ratline, reliant, retinal)
List(caper, crape, pacer, recap)
List(merit, miter, remit, timer)
List(notes, onset, steno, stone)
List(lair, liar, lira, rail)
List(drawer, redraw, reward, warder)

请注意,这使用了第一个建议(字母计数列表),而不是更复杂的位掩码方法。

编辑 2:您可以将哈希函数替换为对每个单词的字符进行简单排序(如 JAB 建议的那样),并使用更清晰/更快的代码获得相同的结果:

def toHash(b:String) = b.toList.sortWith(_<_)

【讨论】:

你能帮我解释一下算法吗。那会很有帮助的。【参考方案3】:

如果你对每个字符的哈希码值进行异或,然后通过输入长度对结果进行异或,无论单词的顺序如何,你都会得到相同的值,这意味着所有的字谜都会产生相同的哈希值。 (长度异或可以防止 'boss' 和 'bo' 返回相同的值,因为 's' 与自身的哈希值始终为 0。)

例子:

int AnagramHash(string input)

    int output = 0;

    foreach(char c in input)
        output ^= c.GetHashCode();

    return output ^ input.Length;

您仍然需要搜索所有具有相同 AnagramHash 的单词。我会使用哈希字段更新字典表(无论您的算法如何)以减少整体计算。

编辑: 另外,作为一个旁注,XOR 是 ALU 执行的最简单的操作,所以如果你最终使用它,你应该能够相当快地生成你的哈希。

【讨论】:

在 C# 中,GetHashCode() 是所有类的方法。它本质上为任何对象生成一个唯一的整数值。 (具有相同值的对象将产生相同的整数。)对于不同的语言,您可以只使用每个字符的字节值作为哈希码,因为它们对于每个值仍然是唯一的。 "您仍然需要搜索所有具有相同 AnagramHash 的单词。"如果您将单词放在列表/等中,则不会。存储在AnagramHash指定的字典中的位置。 如果我使用素数来编码每个字符有什么问题吗?【参考方案4】:

排序和比较不起作用,因为它的时间复杂度非常糟糕。

将时间复杂度换成额外的内存,只需将单词中的字母计数存储在 26-char 中(或您使用的任何语言的等价物,并假设您使用的是罗马字母并且仅字母字符)数组和散列数组。相对于单词长度,您会遇到 O(n) 时间,但大多数英语单词并没有那么长。

例如stacksacktstakc 都会有一个数组,其中的位置为 stack == 1,其余的都设置为 0 .


根据您的评论,这意味着您确实可以对单词的字符进行排序,只要您不对单词本身进行排序,您可以做一些比 Alex 的回答更简单的事情,只需对单词中的字符进行排序字符串并对结果进行哈希处理。 (larsmans 先说了,但没有把它作为答案发布,所以......)

【讨论】:

基本上,我担心时间复杂度。看看其他答案。我认为它会处理这两种复杂性。谢谢 可以,但是你说你不想排序,所以我给你一些不涉及排序的东西。 谢谢。对不起,我迷路了:P Alex 没有对字符进行排序。他正在对单词中的字符进行排序计数,这非常酷。无论如何,谢谢你的帮助。 JAB 是正确的 - 对字符进行排序(只要您仍然保留重复项)并将其用作散列将很好地工作 - 实际上可能比从 chars 到的列表更优雅和高效我建议的很重要。【参考方案5】:

使用以字符串为键并以列表(字符串)为值的哈希图,其中字符串列表包含键字符串的所有字谜。

问题类似于“查找文件中某个单词的所有字谜”

在此处查看算法和代码http://justprogrammng.blogspot.com/2012/06/determine-anagrams-of-word-in-file.html

【讨论】:

以上是关于从字典中获取字谜列表的主要内容,如果未能解决你的问题,请参考以下文章

获取数组中最大的字谜组

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

从字典列表中获取值列表

从字典列表中获取特定字典的值[重复]

从字典列表中获取值

字谜不匹配(字符串到列表),Python