字符串Hash
字符串Hash可以通俗的理解为,把一个字符串转换为一个整数。
最后构造成理想状态下的一个整数→字符串的单射。所以问题就是如何构造Hash函数,使他成为一个单射。
有几种方法,分别是自然溢出方法,单Hash方法和双Hash方法。
我们规定
i
n
d
e
x
(
x
)
=
x
−
′
a
′
+
1
index(x)=x-'a'+1
index(x)=x−′a′+1
说白了就是x是第几个字母
基本Hash公式为
h a s h [ i ] = h a s h [ i − 1 ] ∗ p + i n d e x ( s [ i ] ) hash[i]=hash[i−1]∗p+index(s[i]) hash[i]=hash[i−1]∗p+index(s[i])
自然溢出法
简单来说就是利用unsigned long long的自然溢出对
2
64
取
模
2^{64}取模
264取模
单Hash方法
然后我们有hash公式
h
a
s
h
[
i
]
=
(
h
a
s
h
[
i
−
1
]
)
∗
p
+
i
n
d
e
x
(
s
[
i
]
)
%
m
o
d
hash[i]=(hash[i−1])∗p+index(s[i]) \% mod
hash[i]=(hash[i−1])∗p+index(s[i])%mod
其中p和mod均为质数,且有p<mod
对于此种Hash方法,将p和mod尽量取大即可,这种情况下,冲突的概率是很低的。
例如有字符串"abc"
若取p=13,mod=103,对abc进行hash则有
hash[0]=1
hash[1]=(hash[0]*13+2)%103=15
hash[2]=(hash[1]*13+3)%103=95
此时,串“abc”的hash值为95
为什么要选素数
因为对于取模运算,非素数不能用遍集合里全部元素。
比如我们取12这个合数,我们不难发现,12%它的因子{1,2,3,4,6,12}均为0,对于取模运算,非素数不能用遍集合里全部元素。所以选取合数造成哈希冲突的概率会大大增加。
例如
N=12, g=4, f(g,x)=g*x mod N
4*1 mod N = 4
4*2 mod N = 8
4*3 mod N = 0
4*4 mod N = 4
4*5 mod N = 8
4*6 mod N = 0
4*7 mod N = 4
4*8 mod N = 8
4*9 mod N = 0
4*10 mod N = 4
4*11 mod N = 8
而如果N=13 g=4, f(g,x)=g*x mod N,则
4*1 mod N = 4
4*2 mod N = 8
4*3 mod N = 12
4*4 mod N = 3
4*5 mod N = 7
4*6 mod N = 11
4*7 mod N = 2
4*8 mod N = 6
4*9 mod N = 10
4*10 mod N = 1
4*11 mod N = 5
4*12 mod N = 9
可以直观的看出冲突概率的变化
双Hash方法
将一个字符串用不同的mod hash两次,将这两个结果用一个二元组表示,构建成一个
<
h
a
s
h
1
[
n
]
,
h
a
s
h
[
2
]
>
→
字
符
串
<hash1[n],hash[2]>→字符串
<hash1[n],hash[2]>→字符串
的理想单射
利用两个mod即可
h
a
s
h
1
[
i
]
=
(
h
a
s
h
1
[
i
]
)
∗
p
+
i
n
d
e
x
(
s
[
i
]
)
%
m
o
d
1
hash1[i]=(hash1[i])*p+index(s[i])\%mod1
hash1[i]=(hash1[i])∗p+index(s[i])%mod1
h a s h 2 [ i ] = ( h a s h 2 [ i ] ) ∗ p + i n d e x ( s [ i ] ) % m o d 2 hash2[i]=(hash2[i])*p+index(s[i])\%mod2 hash2[i]=(hash2[i])∗p+index(s[i])%mod2
如何获取子串的Hash
设 有 一 字 符 串 s , l e n ( s ) = 5 , 设 S i 为 第 i 个 字 符 设有一字符串s,len(s)=5,设S_i为第i个字符 设有一字符串s,len(s)=5,设Si为第i个字符
我们根据定义不难得出[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KjGPlFlD-1584246002264)(C:\Users\刘伟强\AppData\Roaming\Typora\typora-user-images\image-20200314104453546.png)]
如
果
我
们
要
求
字
串
S
4
S
5
的
h
a
s
h
值
。
如果我们要求字串S_4S_5的hash值。
如果我们要求字串S4S5的hash值。
不难得出
h
a
s
h
[
4
,
5
]
=
s
4
∗
p
+
s
5
hash[4,5]=s_4*p+s_5
hash[4,5]=s4∗p+s5
由上列可知
h
a
s
h
[
5
]
=
s
1
∗
p
4
+
s
2
∗
p
3
+
s
3
∗
p
2
+
s
4
∗
p
+
s
5
hash[5]=s1*p^4+s_2*p^3+s_3*p^2+s_4*p+s_5
hash[5]=s1∗p4+s2∗p3+s3∗p2+s4∗p+s5
h a s h [ 3 ] = s 1 ∗ p 2 + s 2 ∗ p + s 3 hash[3]=s1*p^2+s_2*p+s_3 hash[3]=s1∗p2+s2∗p+s3
由此可知我们只需要想办法消去hash[5]中S_4之前的部分即可
联立可得
h
a
s
h
[
4
,
5
]
=
h
a
s
h
[
5
]
−
h
a
s
h
[
3
]
∗
p
5
−
4
+
1
hash[4,5]=hash[5]-hash[3]*p^{5-4+1}
hash[4,5]=hash[5]−hash[3]∗p5−4+1
抽象可得
h
a
s
h
[
l
,
r
]
=
h
a
s
h
[
r
]
−
h
a
s
h
[
l
−
1
]
∗
p
r
−
l
+
1
hash[l,r]=hash[r]-hash[l-1]*p^{r-l+1}
hash[l,r]=hash[r]−hash[l−1]∗pr−l+1
注意要每次取模,并且做减法时有可能会出现负数,所以我们对其+mod后再取余做出修正
得到求字串hash值的公式为
h
a
s
h
[
l
,
r
]
=
(
(
h
a
s
h
[
r
]
−
h
a
s
h
[
l
−
1
]
∗
p
r
−
l
+
1
)
%
m
o
d
+
m
o
d
)
%
m
o
d
hash[l,r]=((hash[r]-hash[l-1]*p^{r-l+1})\%mod+mod)\%mod
hash[l,r]=((hash[r]−hash[l−1]∗pr−l+1)%mod+mod)%mod
如何选取素数
像1e9+7,1e9+9的一些素数,出题人一般会专门卡一下
下面这个网站有可供选择的素数,可以自行查阅
我们对其+mod后再取余做出修正
得到求字串hash值的公式为
h
a
s
h
[
l
,
r
]
=
(
(
h
a
s
h
[
r
]
−
h
a
s
h
[
l
−
1
]
∗
p
r
−
l
+
1
)
%
m
o
d
+
m
o
d
)
%
m
o
d
hash[l,r]=((hash[r]-hash[l-1]*p^{r-l+1})\%mod+mod)\%mod
hash[l,r]=((hash[r]−hash[l−1]∗pr−l+1)%mod+mod)%mod
如何选取素数
像1e9+7,1e9+9的一些素数,出题人一般会专门卡一下
下面这个网站有可供选择的素数,可以自行查阅
http://planetmath.org/goodhashtableprimes
在pengwill97大大的https://blog.youkuaiyun.com/pengwill97/article/details/80879387基础上做了一点点自己的理解和扩充
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.youkuaiyun.com/pengwill97/article/details/80879387