字符串哈希
什么是字符串哈希
字符串Hash可以通俗的理解为,把一个字符串转换为一个整数。
如果我们通过某种方法,将字符串转换为一个整数,就可以便的确定某个字符串是否重复出现过,这是最简单的字符串Hash应用情景了。
当然也不难想到,如果有不同的两个字符串同时Hash到一个整数,这样就比较麻烦了。我们希望这个映射是一个单射,所以问题就是如何构造这个Hash函数,使得他们成为一个单射。
Hash方法
给定字符串 s,则 asc[s[i]] = s[i] - ‘a’ + 1.
自然溢出方法
1 | unsigned long long Hash[n]; |
利用unsigned long long的范围自然溢出,相当于自动对 2^64 -1 取模。
单哈希方法
1 | Hash[i] = (Hash[i - 1] * p + asc[s[i]]) % MOD |
其中 p 和 MOD 均为质数,且有 p < MOD。
对于此种Hash方法,将 p 和 MOD 尽量取大即可,这种情况下,冲突的概率是很低的。
如取 p = 13, MOD = 101, 对字符串 “abc” 进行Hash。这里注意asc[s[i]]是s[i]的ascii码加1.
Hash[0] = 1;
Hash[1] = (Hash[0] * 13 + 2) % 101 = 15; // s[i] = ‘b’, asc[s[i]] = ‘b’ - ‘a’ + 1 = 2
Hash[2] = (Hash[1] * 13 + 3) % 101 = 97;
结果是Hash[n],即Hash[2]。所以此时认为 97 就是字符串”abc”的值。
双哈希方法
将一个字符串用不同的MOD Hash两次,将这两个结果用一个二元组表示,作为Hash结果。
1 | Hash1[i] = (Hash[i - 1] * p + asc[s[i]]) % MOD1; |
Hash的结果为 < Hash1[n], Hash2[n] >
。
这种Hash很安全。
求子串公式
假设有一个∣S∣=5 的字符串,设 Si 为第 i 个字符,其中 1 ≤ i ≤ 5。
根据定义分别求出hash[i]:
hash[1] = s1;
hash[2] = s1 ∗ p + s2;
hash[3] = s1 ∗ p^2 + s2 ∗ p + s3;
hash[4] = s1 ∗ p^3 + s2 ∗ p^2 + s3 ∗ p + s4;
hash[5] = s1 ∗ p^4 + s2 ∗ p^3 + s3 ∗ p^2 + s4 ∗ p + s5;
现在要求子串 s3s4 的hash值,不难得出为s3 ∗ p + s4。Hash[4] - Hash[2] * pow(p, 4 - 3 + 1)
所以公式如下:
Hash[l~r] = Hash[r] - Hash[l - 1] * pow(p, r - l + 1)
考虑到取模:
Hash[l~r] = ((Hash[r] - Hash[l - 1] * pow(p, r - l + 1)) % MOD + MOD) % MOD
应用
1 |
|
本文作者 : preccrep
原文链接 : https://preccrep.github.io/2020/11/20/%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%93%88%E5%B8%8C/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!