给定一个字符串abcd
如何创建一个唯一的散列方法,该方法将对这4个字符进行散列以匹配bcad
或字母abcd
的任何其他排列?
目前我有这段代码
long hashString(string a) {
long hashed = 0;
for(int i = 0; i < a.length(); i++) {
hashed += a[i] * 7; // Timed by a prime to make the hash more unique?
}
return hashed;
}
现在这不起作用因为ad
将与bc
一起使用。
我知道你可以通过将字母的位置乘以字母本身hashed += a[i] * i
来使其更独特,但是字符串不会哈希到它的排列。
是否可以创建实现此目的的哈希?
修改
有些人建议在对字符串进行哈希处理之前对其进行排序。这是一个有效的答案,但排序将花费O(nlog)时间,我正在寻找一个在O(n)时间运行的哈希函数。
我希望在O(1)记忆中做到这一点。
答案 0 :(得分:4)
您可以做的基本事情是在应用哈希函数之前对字符串进行排序。因此,要计算“adbc”或“dcba”的哈希值,您需要计算“abcd”的哈希值。
如果要确保散列函数中没有冲突,那么唯一的方法是将散列结果作为字符串。存在比32位(或64位)整数更多的字符串,因此冲突是不可避免的(尽管有良好的散列函数,但不太可能发生冲突)。
答案 1 :(得分:3)
最简单的理解方法:对字符串中的字母进行排序,然后对生成的字符串进行散列。
您原始想法的一些变体也有效,例如:
long hashString(string a) {
long hashed = 0;
for(int i = 0; i < a.length(); i++) {
long t = a[i] * 16777619;
hashed += t^(t>>8);
}
return hashed;
}
答案 2 :(得分:2)
我想你需要一个散列,这样两个字谜就会散列到相同的值。我建议你先排序它们并使用任何常见的哈希函数,如md5。我使用Scala编写以下代码:
import java.security.MessageDigest
def hash(s: String) = {
MessageDigest.getInstance("MD5").digest(s.sorted.getBytes)
}
在scala中注意:
scala> "hello".sorted
res0: String = ehllo
scala> "cinema".sorted
res1: String = aceimn
答案 3 :(得分:2)
创建一个包含26个整数的数组,对应于字母a-z
。将其初始化为0.从头到尾扫描字符串,并递增与当前字母对应的数组元素。请注意,到目前为止,算法具有O(n)
时间复杂度和O(1)
空间复杂度(因为数组大小是常量)。
最后,使用您喜欢的哈希函数散列数组的内容。
答案 4 :(得分:2)
概要:在哈希值中存储字母的直方图。
步骤1:计算字母的直方图(因为直方图唯一地标识字符串中的字母而不考虑字母的顺序)。
int histogram[26];
for ( int i = 0; i < a.length(); i++ )
histogram[a[i] - 'a']++;
步骤2:将直方图打包到哈希值中。你有几个选择。选择哪个选项取决于您可以对字符串施加什么样的限制。
如果您知道每个字母的出现次数不会超过3次,那么表示计数需要2位,因此您可以创建一个保证唯一的52位散列。
如果您愿意使用128位散列,则24个字母有5位,2个字母有4位(例如q
和z
)。 128位哈希允许每个字母出现31次(q
和z
的15次)。
但是如果你想要一个固定大小的散列,比如说16位,那么你需要以减少冲突的方式将直方图打包成这16位。最简单的方法是创建一个26字节的消息(直方图中每个条目一个字节,允许每个字母最多出现255次)。然后使用您最喜欢的CRC生成器获取消息的16位CRC。