给定一组单词,我们需要找到anagram单词并使用最佳算法单独显示每个类别。
输入:
man car kile arc none like
输出:
man
car arc
kile like
none
我现在开发的最佳解决方案是基于散列表,但我正在考虑将anagram字转换为整数值的等式。
示例:man => 'm'+'a'+'n'但这不会给出唯一的值。
有什么建议吗?
请参阅C#中的以下代码:
string line = Console.ReadLine();
string []words=line.Split(' ');
int[] numbers = GetUniqueInts(words);
for (int i = 0; i < words.Length; i++)
{
if (table.ContainsKey(numbers[i]))
{
table[numbers[i]] = table[numbers[i]].Append(words[i]);
}
else
{
table.Add(numbers[i],new StringBuilder(words[i]));
}
}
问题是如何开发GetUniqueInts(string [])
方法。
答案 0 :(得分:39)
根本不需要使用自定义哈希函数。在您的平台上使用普通的字符串哈希函数。重要的是使哈希表的关键字成为“排序单词”的概念 - 其中单词按字母排序,因此“car”=&gt; “ACR”。所有字谜都将具有相同的“排序字”。
只需从“已排序的单词”到“该排序单词的单词列表”的哈希值。在LINQ中,这非常容易:
using System;
using System.Collections.Generic;
using System.Linq;
class FindAnagrams
{
static void Main(string[] args)
{
var lookup = args.ToLookup(word => SortLetters(word));
foreach (var entry in lookup)
{
foreach (var word in entry)
{
Console.Write(word);
Console.Write(" ");
}
Console.WriteLine();
}
}
static string SortLetters(string original)
{
char[] letters = original.ToCharArray();
Array.Sort(letters);
return new string(letters);
}
}
样品使用:
c:\Users\Jon\Test>FindAnagrams.exe man car kile arc none like
man
car arc
kile like
none
答案 1 :(得分:18)
我使用了Godel启发的方案:
将素数P_1到P_26分配给字母(以任何顺序,但要获得较小的哈希值,最好给出普通字母小素数)。
构建单词中字母的直方图。
然后,哈希值是每个字母的相关素数与其频率幂的乘积。这为每个字谜提供了独特的价值。
Python代码:
primes = [2, 41, 37, 47, 3, 67, 71, 23, 5, 101, 61, 17, 19, 13, 31, 43, 97, 29, 11, 7, 73, 83, 79, 89, 59, 53]
def get_frequency_map(word):
map = {}
for letter in word:
map[letter] = map.get(letter, 0) + 1
return map
def hash(word):
map = get_frequency_map(word)
product = 1
for letter in map.iterkeys():
product = product * primes[ord(letter)-97] ** map.get(letter, 0)
return product
这巧妙地将寻找subanagrams的棘手问题转化为(也称为棘手的)分解大数字的问题......
答案 2 :(得分:7)
giggles的Python版本:
from collections import defaultdict
res = defaultdict(list)
L = "car, acr, bat, tab, get, cat".split(", ")
for w in L:
res["".join(sorted(w))].append(w)
print(res.values())
答案 3 :(得分:3)
我认为你找不到比带有自定义散列函数的散列表更好的东西(在散列之前会对单词的字母进行排序)。
这些字母的总和永远不会有效,因为你不能真正使'ac'和'bb'不同。
答案 4 :(得分:3)
您将需要大整数(或实际位矢量),但以下可能有效
每个字母的第一次出现都被赋予了该字母的位号,第二次出现该字母的位数+ 26。
例如
a#1 = 1 b#1 = 2 c#1 = 4 a#2 = 2 ^ 26 b#2 = 2 ^ 27
然后你可以将它们加在一起,根据它的字母获得单词的唯一值。
您对字值的存储要求为:
n * 26位
其中n是任何重复字母的最大出现次数。
答案 5 :(得分:2)
我不会使用散列,因为它增加了查找和添加的额外复杂性。散列,排序和乘法都比基于数组的简单直方图解决方案慢,跟踪唯一性。最坏的情况是O(2n):
// structured for clarity
static bool isAnagram(String s1, String s2)
{
int[] histogram = new int[256];
int uniques = 0;
// scan first string
foreach (int c in s1)
{
// count occurrence
int count = ++histogram[c];
// count uniques
if (count == 1)
{
++uniques;
}
}
// scan second string
foreach (int c in s2)
{
// reverse count occurrence
int count = --histogram[c];
// reverse count uniques
if (count == 0)
{
--uniques;
}
else if (count < 0) // trivial reject of longer strings or more occurrences
{
return false;
}
}
// final histogram unique count should be 0
return (uniques == 0);
}
答案 6 :(得分:1)
我之前用一个简单的字母数组来实现它,例如:
unsigned char letter_frequency[26];
然后将其与每个单词一起存储在数据库表中。具有相同字母频率'signature'的单词是anagrams,然后简单的SQL查询直接返回单词的所有字谜。
通过对一个非常大的字典的一些实验,我发现任何字母都没有超过频率计数9的字,所以'签名'可以表示为一串数字0..9(大小可以很容易通过以十六进制形式打包成字节减半,并通过二进制编码进一步减少数字,但到目前为止我没有打扰任何一个。)
这是一个ruby函数,用于计算给定单词的签名并将其存储到Hash中,同时丢弃重复项。从Hash我稍后构建一个SQL表:
def processword(word, downcase)
word.chomp!
word.squeeze!(" ")
word.chomp!(" ")
if (downcase)
word.downcase!
end
if ($dict[word]==nil)
stdword=word.downcase
signature=$letters.collect {|letter| stdword.count(letter)}
signature.each do |cnt|
if (cnt>9)
puts "Signature overflow:#{word}|#{signature}|#{cnt}"
end
end
$dict[word]=[$wordid,signature]
$wordid=$wordid+1
end
end
答案 7 :(得分:1)
为字母a-z
指定唯一的素数迭代你的单词数组,根据每个单词中的字母创建素数的乘积 将该产品存储在您的单词列表中,并附上相应的单词。
对数组进行排序,按产品递增。
迭代数组,在每次产品更改时都执行control break。
答案 8 :(得分:0)
在C中,我刚刚实现了以下哈希,它基本上对字典中的单词是否包含特定字母进行了26位位掩码。所以,所有字谜都有相同的哈希值。哈希没有考虑重复的字母,因此会有一些额外的重载,但它仍然比我的perl实现更快。
#define BUCKETS 49999
struct bucket {
char *word;
struct bucket *next;
};
static struct bucket hash_table[BUCKETS];
static unsigned int hash_word(char *word)
{
char *p = word;
unsigned int hash = 0;
while (*p) {
if (*p < 97 || *p > 122) {
return 0;
}
hash |= 2 << (*p - 97);
*p++;
}
return hash % BUCKETS;
}
重载桶创建并添加为链表等。然后只需编写一个函数,确保与哈希值匹配的单词长度相同,并且每个单词中的字母为1到1,并将其作为匹配返回
答案 9 :(得分:0)
我将根据示例单词和其他我不关心的字母表生成hasmap。
例如,如果单词是“car” 我的哈希表将是这样的: 一,0 B,MAX C,1 d,MAX E,MAX ... .. R,2 。 因此,任何大于3的人都会认为不匹配
(更多调整......) 我的比较方法将比较哈希计算本身内的哈希总数。一旦它能识别出不相等的词,它就不会继续。
public static HashMap<String, Integer> getHashMap(String word) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
String[] chars = word.split("");
int index = 0;
for (String c : chars) {
map.put(c, index);
index++;
}
return map;
}
public static int alphaHash(String word, int base,
HashMap<String, Integer> map) {
String[] chars = word.split("");
int result = 0;
for (String c : chars) {
if (c.length() <= 0 || c.equals(null)) {
continue;
}
int index = 0;
if (map.containsKey(c)) {
index = map.get(c);
} else {
index = Integer.MAX_VALUE;
}
result += index;
if (result > base) {
return result;
}
}
return result;
}
主要方法
HashMap<String, Integer> map = getHashMap(sample);
int sampleHash = alphaHash(sample, Integer.MAX_VALUE, map);
for (String s : args) {
if (sampleHash == alphaHash(s, sampleHash, map)) {
System.out.print(s + " ");
}
}
答案 10 :(得分:0)
Anagrams可以通过以下方式找到:
所以我想通过以上三个验证,我们可以找到字谜。如果我错了,请纠正我。
示例:abc cba
两个单词的长度均为3.
两个单词的单个字符总和为294。
两个单词的个别字符的产生是941094.
答案 11 :(得分:0)
除了其他有用的答案之外,只想添加简单的python解决方案:
def check_permutation_group(word_list):
result = {}
for word in word_list:
hash_arr_for_word = [0] * 128 # assuming standard ascii
for char in word:
char_int = ord(char)
hash_arr_for_word[char_int] += 1
hash_for_word = ''.join(str(item) for item in hash_arr_for_word)
if not result.get(hash_for_word, None):
result[str(hash_for_word)] = [word]
else:
result[str(hash_for_word)] += [word]
return list(result.values())
答案 12 :(得分:0)
python代码:
line = "man car kile arc none like"
hmap = {}
for w in line.split():
ws = ''.join(sorted(w))
try:
hmap[ws].append(w)
except KeyError:
hmap[ws] = [w]
for i in hmap:
print hmap[i]
输出:
['car', 'arc']
['kile', 'like']
['none']
['man']
答案 13 :(得分:-1)
JavaScript版本。使用散列。
时间复杂度:0(nm),其中n是单词的数量,m是单词的长度
var words = 'cat act mac tac ten cam net'.split(' '),
hashMap = {};
words.forEach(function(w){
w = w.split('').sort().join('');
hashMap[w] = (hashMap[w]|0) + 1;
});
function print(obj,key){
console.log(key, obj[key]);
}
Object.keys(hashMap).forEach(print.bind(null,hashMap))