字符串Hash入门
Hash方法
自然溢出方法
Hash公式
单Hash方法
Hash公式
举例
双Hash方法
Hash公式
获取子串的Hash
例子
公式
字符串Hash的应用
题型一
描述
解法
题型二
描述
解法
题型三
描述
解法
题型四
描述
解法
Hash素数的选取
字符串Hash入门
字符串Hash可以通俗的理解为,把一个字符串转换为一个整数。
如果我们通过某种方法,将字符串转换为一个整数,就可以便的确定某个字符串是否重复出现过,这是最简单的字符串Hash应用情景了。
当然也不难想到,如果有不同的两个字符串同时Hash到一个整数,这样就比较麻烦了。我们希望这个映射是一个单射,所以问题就是如何构造这个Hash函数,使得他们成为一个单射。不用担心,接下来的内容正要讲解。
Hash方法
给定一个字符串S=s1s2s3..snS=s1s2s3..sn,对字母x,我们规定idx(x)=x−′a′+1idx(x)=x−′a′+1。 (当然也可以直接用sisi的ASCIIASCII值)
自然溢出方法
Hash公式
unsigned long long Hash[n]
hash[i]=hash[i−1]∗p+id(s[i])hash[i]=hash[i−1]∗p+id(s[i])
利用unsigned long long的范围自然溢出,相当于自动对264−1264−1取模
单Hash方法
Hash公式
hash[i]=(hash[i−1])∗p+idx(s[i]) % modhash[i]=(hash[i−1])∗p+idx(s[i]) % mod
其中pp和modmod均为质数,且有p<modp<mod。
对于此种Hash方法,将p和mod尽量取大即可,这种情况下,冲突的概率是很低的。
举例
如取p=13,mod=101p=13,mod=101,对字符串abcabc进行Hash
hash[0] = 1
hash[1] = (hash[0] * 13 + 2) % 101 = 15
hash[2] = (hash[1] * 13 + 3) % 101 = 97
这样,我们就认为字符串abcabc当做97,即97就是abcabc 的hash值。
双Hash方法
将一个字符串用不同的modmod hash两次,将这两个结果用一个二元组表示,作为Hash结果。
Hash公式
hash1[i]=(hash1[i−1])∗p+idx(s[i]) % mod1hash1[i]=(hash1[i−1])∗p+idx(s[i]) % mod1
hash2[i]=(hash2[i−1])∗p+idx(s[i]) % mod2hash2[i]=(hash2[i−1])∗p+idx(s[i]) % mod2
hash结果为<hash1[n],hash2[n]><hash1[n],hash2[n]>
这种Hash很安全。
获取子串的Hash
如果我们求出一个串的Hash,就可以O(1)O(1)求解其子串的Hash值。
我们先以一个具体的例子来理解。
例子
假设有一|S|=5|S|=5的字符串,设SiSi为第ii个字符,其中1≤i≤51≤i≤5。
根据定义分别求出hash[i]hash[i]
hash[1]=s1hash[1]=s1
hash[2]=s1∗p+s2hash[2]=s1∗p+s2
hash[3]=s1∗p2+s2∗p+s3hash[3]=s1∗p2+s2∗p+s3
hash[4]=s1∗p3+s2∗p2+s3∗p+s4hash[4]=s1∗p3+s2∗p2+s3∗p+s4
hash[5]=s1∗p4+s2∗p3+s3∗p2+s4∗p+s5hash[5]=s1∗p4+s2∗p3+s3∗p2+s4∗p+s5
现在我们想求s3s4s3s4的hash值,不难得出为s3∗p+s4s3∗p+s4,并且从上面观察,如果看hash[4]−hash[2]hash[4]−hash[2]并将结果种带有s1,s2s1,s2系数的项全部消掉,就是所求。但是由于pp的阶数,不能直接消掉,所以问题就转化成,将hash[2]hash[2]乘一个关于pp的系数,在做差的时候将多余项消除,从而得到结果。
不难发现,对应项系数只差一个p2p2,而4 - 3 + 1 = 2(待求hash子串下标相减再加一),这样就不难推导出来此例题的求解式子。
hash[4]−hash[2]∗p4−2+1
hash[4]−hash[2]∗p4−2+1
至此,通过对上例的归纳,可以得出如下的公式。
公式
若已知一个|S|=n|S|=n的字符串的hash值,hash[i],1≤i≤nhash[i],1≤i≤n,其子串sl..sr,1≤l≤r≤nsl..sr,1≤l≤r≤n对应的hash值为:
hash=hash[r]−hash[l−1]∗pr−l+1
hash=hash[r]−hash[l−1]∗pr−l+1
考虑到hash[i]hash[i]每次对pp取模,进一步得到下面的式子:
hash=(hash[r]−hash[l−1]∗pr−l+1)%MOD
hash=(hash[r]−hash[l−1]∗pr−l+1)%MOD
看起来这个式子人畜无害,但是对于取模运算要谨慎再谨慎,注意到括号里面是减法,即有可能是负数,故做如下的修正:
hash=((hash[r]−hash[l−1]∗pr−l+1)%MOD+MOD)%MOD
hash=((hash[r]−hash[l−1]∗pr−l+1)%MOD+MOD)%MOD
至此得到求子串hash值公式。
值得一提的是,如果需要反复对子串求解hash值,预处理pp的nn次方效果更佳。
字符串Hash的应用
题型一
描述
问题:给两个字符串S1,S2,求S2是否是S1的子串,并求S2在S1中出现的次数
数据范围:1=<|S1|,|S2|<=10000
解法
求出S1和S2的Hash值,并且n2n2的求解出S1所有子串的Hash值,放入map中,查询即可。复杂度n2lognn2logn
题型二
描述
问题:给N个单词串,和一个文章串,求每个单词串是否是文章串的子串,并求每个单词在文章中出现的次数。
数据范围:文章串长度:[1,10^5],N个单词串总长:[1,10^6]
解法
设单词串总长为|S||S|,文章串总长为|A||A|。
此题和第一题做法相同。复杂度|A|2log|A|+|S||A|2log|A|+|S|
题型三
描述
问题:给两个字符串S1,S2,求它们的最长公共子串的长度。
数据范围:1=<|S1|,|S2|<=10^5
解法
将S1的每一个子串都hash成一个整数
将S2的每一个子串都hash成一个整数
两堆整数,相同的配对,并且找到所表示的字符串长度最大的即可。
复杂度:O(|S1|2+|S2|2)O(|S1|2+|S2|2)
PS:为觉得开数组不保险,所以上面的题一和题二都用的map存,这里我也不知道能不能实现O(1)O(1)的存储和查询。
题型四
描述
问题:给一个字符串S,求S的最长回文子串。
比如abcbbabbc的最长回文子串是cbbabbc,bbabb也是回文串,但不是最长的
数据范围: 1=<|S|<=10^5
解法
先求子串长度位奇数的,再求偶数的。枚举回文子串的中心位置,然后二分子串的长度,直到找到一个该位置的最长回文子串,不断维护长度最大值即可。
复杂度:O(|S|∗log|S|)O(|S|∗log|S|)
Hash素数的选取
为了防止冲突,要选择合适的素数,像1e9+7,1e9+9的一些素数,出题人一般会卡一下下,所以尽量选择其他的素数,防止被卡。下面是一些可供选择的素数。
上界和下界指的是离素数最近的2n2n的值。
lwr(下界) upr(上界) %err(冲突率) prime(素数)
2525 2626 10.416667 53
2626 2727 1.0416670 97
2727 2828 0.520833 193
2828 2929 1.302083 389
2929 210210 0.130208 769
210210 211211 0.455729 1543
211211 212212 0.227865 3079
212212 213213 0.113932 6151
213213 214214 0.008138 12289
214214 215215 0.069173 24593
215215 216216 0.010173 49157
216216 217217 0.013224 98317
217217 218218 0.002543 196613
218218 219219 0.006358 393241
219219 220220 0.000128 786433
220220 221221 0.000318 1572869
221221 222222 0.000350 3145739
222222 223223 0.000207 6291469
223223 224224 0.000040 12582917
224224 225225 0.000075 25165843
225225 226226 0.000010 50331653
226226 227227 0.000023 100663319
227227 228228 0.000009 201326611
228228 229229 0.000001 402653189
229229 230230 0.000011 805306457
230230 231231 0.000000 1610612741
来源 :http://planetmath.org/goodhashtableprimes
————————————————
版权声明:本文为CSDN博主「pengwill97」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pengwill97/article/details/80879387