2012-06-19 64 views
5

基本上,Anagrams类似于字符串的排列。例如,stack,sackt,stakc都是stack的字典(认为上面的单词没有意义)。无论如何,你可以理解我的意思。从字典中获得anagrams列表

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

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

排序和比较 是行不通的,因为它的时间复杂度是非常糟糕的。

我想过使用散列表,字符串作为关键。

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

谢谢。

+1

问题不可怕在这里清楚。你能否重新说明目标? –

+0

你的意思是说:我有一百万字的字典,我希望能够识别字典中相互对峙的所有单词集?例如。如果字典中包含:[tap,pat,pot,top],您希望看到[[tap,pat],[pot,top]]? –

+0

是啊@Alex。我只想要有多少不同的字谜? – vijay

回答

20

显而易见的解决方案是将每个字符映射到素数并乘以素数。因此,如果 'A'” - > 2和 'b' - > 3,则

  • 'AB' - > 6
  • 'BA' - > 6
  • 'BAB' - > 18
  • 'ABBA' - > 36
  • '巴巴' - > 36

为了最小化溢出的可能性,最小素数可分配给更频繁的字母(例如,T,I,A,N )。注意:第26个素数是101。

UPDATE: an implementation can be found here

+0

它似乎cool.thanx。 – vijay

+1

你仍然必须处理溢出,这可能会导致“冲突”。可能通过存储每个条目的字母频率直方图。 – wildplasser

+0

是的,我明白了。但我发现你的方法很酷。 – vijay

2

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

或者,您可以通过从您的词中生成一个位掩码来获得一个不精确的分组,其中位0-25表示该字母的存在或不存在(位0表示'a'至位25表示'z') 。但是,接下来你需要做更多的处理来分割每个哈希组以进一步区分例如从“太”到“从”。

这些想法都有帮助吗?记住任何特定的实现语言(我可以做C++,Python或Scala)?

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

OK:我在此刻Scala的模式,所以我敲东西了,做你问什么,但(啊哈)它如果你不熟悉Scala或函数式编程,可能不太清楚。

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

我对他们的运行此Scala代码(发生在脚本模式下使用的Scala 2.9,包括时间编制约5秒,约40000字的字典不是最有效的代码,而是首先想到的)。

// 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:您可以在每个字(如联合申诉委员会的建议)的字符一个简单的排序代替散列函数和更清晰的/更快的代码获得相同的结果:

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

看起来很酷。伪代码会很好。谢谢 – vijay

+0

你能帮我解释一下这个算法吗?那会非常有帮助。 – vijay

+0

我不知道Scala.Anyways感谢您的努力。 – vijay

0

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

交换的时间复杂度为额外内存,只是存储的字母数一句话在26- char(或者你使用的任何一种语言,并假设相当于你正在使用罗马字母和只有字母字符)数组并散列该数组。你相对于单词长度而言被困在O(n)时间,但大多数英语单词并不是真的那么长。

例如stacksacktstakc都将必须在该位置阵列stack == 1,其余都设置为0。


基于您的评论,这意味着只要你自己没有对单词进行排序,你的确可以对单词的字符进行排序,你可以做一些比Alex的回答更简单的事情,只需对单词串中的字符进行排序并对结果进行散列即可。 (larsmans首先说,但没有发布它作为答案,所以...)

+0

基本上,我关心时间复杂性。并看看其他答案。我认为它会考虑到这两个复杂性。谢谢 – vijay

+1

它确实,但你说你不想排序,所以我给你一些东西不涉及排序。 – JAB

+0

Thanks.Sorry我迷路了:P – vijay

1

如果你异或每个字符的散列码值,然后XOR结果的输入长度,你会得到无论单词的顺序如何,都是相同的值,这意味着所有的anagrams将产生相同的散列值。 (异或由长度防止“老板”和“博”从返回相同的值,因为“s”的自相的哈希始终为0)

实施例:

int AnagramHash(string input) 
{ 
    int output = 0; 

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

    return output^input.Length; 
} 

您仍必须搜索具有相同AnagramHash的所有单词。我会用字段来更新字典表(不管你的算法是什么),以减少整体计算。另外,作为一个便笺,XOR是ALU执行的最简单的操作,所以如果你最终使用它,你应该能够相当快地生成你的哈希值。

+0

你如何得到唯一的哈希码? – vijay

+0

在C#中'GetHashCode()'是所有类的一个方法。它本质上为任何对象生成一个唯一的整数值。 (具有相同值的对象将生成相同的整数。)对于不同的语言,您可以将每个字符的字节值用作哈希码,因为它们对于每个值仍然是唯一的。 –

+0

“您仍然必须使用相同的AnagramHash搜索所有单词。”不,如果你把这些单词放在列表中/等。它们存储在由'AnagramHash'指定的字典中的位置。 – JAB