基本上,Anagrams就像是字符串的排列.E.g stack
,sackt
,stakc
都是stack
的字谜(上面的想法是没有意义的)。无论如何,你可以理解我的意思。
现在,我想要一个anagrams
列表给出百万字,或者只是从字典中说出来。
我的基本问题是Find total number of unique anagrams in a dictionary?
排序和比较 不会起作用,因为它的时间复杂性非常糟糕。
我想过使用哈希表,字符串作为键。
但问题是哈希函数应该是什么?如果有一些伪代码会有所帮助 提供。比提到的方法更好的其他一些方法也会有所帮助。
感谢。
答案 0 :(得分:23)
显而易见的解决方案是将每个字符映射到素数并乘以素数。所以如果'a'' - > 2和'b' - > 3,然后
为了最大限度地减少溢出的可能性,可以将最小的素数分配给更频繁的字母(e,t,i,a,n)。注意:第26个素数是101。
答案 1 :(得分:2)
一个可能的哈希函数可以是(仅假设英语单词)每个字母出现次数的排序计数。所以对于“anagram”你会产生[('a',3),('g',1),('n',1),('m',1),('r',1)]。
或者你可以通过从你的单词生成一个位掩码得到一个不精确的分组,其中0-25位表示该字母的存在或不存在(位0代表'a'到位25代表'z')。但是,你必须进行更多的处理以进一步分割每个散列组以区分例如从“太”开始“到”。
这些想法中的任何一个都有帮助吗?任何特定的实现语言(我可以做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(_<_)
答案 2 :(得分:1)
如果对每个字符的哈希码值进行异或,然后按输入长度对结果进行异或,则无论单词的顺序如何,都将获得相同的值,这意味着所有字符串将产生相同的哈希值。 (长度的异或会阻止'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执行的最简单的操作,因此如果您最终使用它,您应该能够相当快地生成哈希值。
答案 3 :(得分:0)
排序和比较不会起作用,因为它的时间复杂度非常糟糕。
交换额外内存的时间复杂度,只需将单词中字母的计数存储在26 - char
(或等同于您正在使用的任何语言中,并假设您使用的是罗马字母并且只有字母字符)数组和散列数组。你的O(n)时间相对于单词长度而言是困难的,但大多数英语单词并不那么长。
e.g。 stack
,sackt
和stakc
都会有一个数组,其中包含s
,t
,a
,c
的位置,k
== 1,其余全部设为0。
根据你的评论,这意味着只要你不自己整理单词,你就可以对单词的字符进行排序,你可以做一些比Alex的答案更简单的事情,只需对单词中的字符进行排序字符串和哈希结果。 (larsmans首先说,但没有把它作为答案发布,所以......)
答案 4 :(得分:0)
使用带有字符串作为键的hashmap和list(string)作为值,其中字符串列表包含键字符串的所有字符。
问题类似于“查找文件中某个单词的所有字谜”
在此处查看算法和代码http://justprogrammng.blogspot.com/2012/06/determine-anagrams-of-word-in-file.html