字符串哈希总结

昨天做了学长拉的字符串哈希的专题,今天便来做个总结吧。

哈希是数据结构里面的内容,其实就是把字符串编个号,用数字来表示,这样查询和判断相同时就方便了许多。

但是编的这个号也是有很关键,我们需要尽可能的保证不同字符串的编号不会相等,即冲突概率为0.

一个关键的定理:哈希值相等的字符串不一定相等,字符串相等的哈希值一定相等。因此我们需要尽可能减少第一种情况。

在处理子串问题时,我们一般采用前缀和去维护哈希值(当然,你可以使用树状数组啊,平衡树啊,去动态维护整个序列),因为它是比较简单易于实现的哈希函数(当然也有其他哈希函数,详情请参考:字符串哈希函数)

下面的讲解都是根据前缀和来实现的。


常用的哈希方法有三种:自然溢出,单哈希和双哈希。

一:自然溢出

自然溢出其实就是用unsigned long long来代替了对质数取余这一步骤,unsigned long long本身范围就是2^64-1,也相当与对一个大质数取余了,其实到了一样。不过因为简单易于实现,最常用这种方法来实现。

先声明hash[maxn]的类型为unsigned long long,哈希函数hash[i]=hash[i-1]*p+s[i]-'a'+1(s为字符串)

二:单哈希

单哈希是比较经典的哈希方法,hash[i]=(hash[i-1]*p+s[i]-'a'+1)%mod(p,mod为质数,而且p

三:双哈希

顾名思义,用两个哈希数组hash1,hash2和两个mod1,mod2来实现,结果用一个二元组表示

hash1[i]=(hash1[i−1])∗p+s[i]-'a'+1) % mod1

hash2[i]=(hash2[i−1])∗p+s[i]-'a'+1) % mod2

hash结果为

(备注:为什么要乘于p,相当于把字符串看成一个p进制的数,类比16进制。哈希值就是进制转换之后对mod取余的结果


下面讲一个哈希算法的经典用法,就是取出字符串的子串的哈希值。

先抛出代码!!

hash[l~r] = hash[1~r] - hash[1~l - 1] * pow(P, r - l + 1); 

看起来这个式子人畜无害,但是对于取模运算要谨慎再谨慎,注意到括号里面是减法,即有可能是负数,故做如下的修正:    hash=((hash[r]−hash[l−1]∗pr−l+1)%MOD+MOD)%MOD;

至此得到求子串hash值公式。

 

当然处理的时候,求p的多少次方,我们都会预处理一下,这个代码的思想大家理解一下就应该会懂。

下面给出证明:(来自网上大佬)

例子
假设有一个|S|=5的字符串,设Si为第i个字符,其中1≤i≤5。
根据定义分别求出hash[i]
hash[1]=s1
hash[2]=s1∗p+s2
hash[3]=s1∗p2+s2∗p+s3
hash[4]=s1∗p3+s2∗p2+s3∗p+s4 
hash[5]=s1∗p4+s2∗p3+s3∗p2+s4∗p+s5

现在我们想求s3s4的hash值,不难得出为s3∗p+s4,并且从上面观察,
如果看hash[4]−hash[2]并将结果种带有s1,s2系数的项全部消掉,就是所求。
但是由于p的阶数,不能直接消掉,所以问题就转化成,将hash[2]乘一个关于p的系数,
在做差的时候将多余项消除,从而得到结果。

不难发现,对应项系数只差一个p2,而4 - 3 + 1 = 2(待求hash子串下标相减再加一),
这样就不难推导出来此例题的求解式子。
hash[4]−hash[2]∗p^(4−2+1)

关于哈希算法的好的板子,大家可以网上百度一下kuangbin大佬的板子,我感觉挺不错的。

单独考哈希的题出的很少,而且有的其他方法也可以实现,不过哈希还是很有用的,是其他的基础。

哈希常常和二分一起出现,后面的题也有。


下面抛出一些经典的哈希题。

字符串哈希
hdu1880——哈希+恶心输入输出 题解
POJ1743——哈希+二分(后缀数组SA) 题解
POJ3461——经典哈希(求可重叠串个数) 题解
POJ2406——经典哈希(求最大循环次数) 题解
POJ2752——经典哈希(公共前缀后缀长度) 题解
SCU4438——经典哈希+前缀和 题解
hdu1496——哈希妙用(暴力+优化) 题解

终于搞完了!!!!

 

 

 

 

 

 

你可能感兴趣的:(字符串哈希,算法学习总结,字符串哈希)