牛客字符串班笔记
Hash 定义
定义
Hash 是一种单射函数,可以将万物单向映射成一个整数值。
字符串 Hash 是指将一个字符串串映射成一个整数值,用于快速比较字符串是否相等。
H(S):S的 Hash值,即映射后的整数值。
性质
必要性:若 S = T,一定有 H(S) = H(T)
非充分性:若 H(S) = H(T),不一定有 S = T
Hash 检测
Hash 检测:通过 H(S) 和 H(T) 是否相等,来判断 S 和 T 是否相等的方法。
Hash 冲突:H(S) = H(T) ,但是 S ≠ T,即为发生 Hash 冲突。
Hash 检测时发生 Hash 冲突的概率是衡量 Hash 算法好坏的重要指标
设计 Hash 算法
多项式 Hash
将字符串看作是某个进制(Base)下的字符串。
H ( S ) = ∑ i = 1 i ≤ ∣ s ∣ = n S [ i ] ∗ B a s e 1 + n − i H(S)=\sum_{i=1}^{i \leq |s|=n} S[i]*Base^{1+n-i} H(S)=∑i=1i≤∣s∣=nS[i]∗Base1+n−i
= H ( S [ 1 , ∣ S ∣ − 1 ] ) ∗ B a s e + S [ ∣ S ∣ ] ~~~~~~~~~~~=H(S[1,|S|-1])*Base+S[|S|] =H(S[1,∣S∣−1])∗Base+S[∣S∣]
= S [ 1 ] ∗ B a s e n + S [ 2 ] ∗ B a s e n − 1 + . . . + S [ n ] ∗ B a s e 0 ~~~~~~~~~~~=S[1]*Base^n+S[2]*Base^{n-1}+...+S[n]*Base^0 =S[1]∗Basen+S[2]∗Basen−1+...+S[n]∗Base0
栗子:
字符集 ∑ = a , b , , . . . , o \sum = {a,b,,...,o} ∑=a,b,,...,o分别对应十六进制的 1 2 … F
若 S = adenoo,则 H ( S ) = 145 E F ( 16 ) = 133503 9 ( 10 ) H(S) = 145EF_{(16)}=1335039_{(10)} H(S)=145EF(16)=1335039(10)
优点:字符串和 Hash 值一一对应,无 Hash 冲突
缺点:数据范围过大,难以存储和比较
多项式取模 Hash(模哈)
模哈是为了解决多项式 Hash 的缺点,在效率和冲突率之间进行的折中:
将多项式 Hash 的值对一个较大的质数取模。
H‘(S) = H(S) % Mod
优点:多项式 Hash 可以存储。
缺点:小概率发生 Hash 冲突。
模哈冲突概率
模哈冲突:H(S) ≠ H(T) 但 H(S) % Mod = H(T) % Mod
H(S) 被随机映射成 [1, Mod-1] 内的整数。
生日悖论
有 n 个人,他们的生日是 [1,365] 中的随机整数
若 n > 365 n > 365 n>365,一定有两个人生日相同
若 n ≤ 365 n \leq 365 n≤365,没有人生日相同的概率为 A ( 365 , n ) 36 5 n \frac{A(365,n)}{365^n} 365nA(365,n)
当 n = 23 时,上述结果约为 0.46,即有超过 50% 的概率有人生日相同。
即当检验次数超过 M o d \sqrt{Mod} Mod,就会有较大概率发生错误。
因此,在模哈中使用的 Mod 最好超过 Hash 检验次数的平方、
Hash 的三种姿势
Hash 模数
优秀的 Hash 模数应该 满足:足够大
自然溢出:ULL保存 Hash 值,利用硬件特性,使 Hash 值自然溢出,即 M o d = 2 64 Mod = 2^{64} Mod=264,但容易造成 Hash 冲突 。
优秀的 Hash 模数还应该是质数
当模数是合数,相当于选了很多小模数,Hash 冲突的概率为 n k p 1 ∗ p 2 ∗ . . . ∗ p k \frac{n^k}{p_1*p_2*...*p_k} p1∗p2∗...∗pknk,否则为 n p \frac{n}{p} pn
单模
选取 1 0 9 10^9 109到 1 0 1 0 10^10 1010范围的大质数作为 Hash 模数。但也有广为人知的方法构造冲突。
双模
进行多次不同质数的单模 Hash,降低冲突率。
在不泄露模数的前提,没有已知方法可以构造冲突。
子串 Hash
H ( s [ l , r ] ) = ( S [ l ] ∗ B a s e r − l + S [ l + 1 ] ∗ B a s e r − l − 1 + . . . + S [ r ] ) H(s[l,r])=(S[l]*Base^{r-l}+S[l+1]*Base^{r-l-1}+...+S[r]) H(s[l,r])=(S[l]∗Baser−l+S[l+1]∗Baser−l−1+...+S[r])%Mod
令 F ( i ) = H ( p r e [ i ] ) F(i) = H(pre[i]) F(i)=H(pre[i])
F ( l − 1 ) = ( S [ 1 ] ∗ B a s e l − 2 + S [ 2 ] ∗ B a s e l − 3 + . . . + S [ l − 1 ] ) F(l-1)=(S[1]*Base^{l-2}+S[2]*Base^{l-3}+...+S[l-1]) F(l−1)=(S[1]∗Basel−2+S[2]∗Basel−3+...+S[l−1])% Mod
F ( r ) = ( S [ 1 ] ∗ B a s e r − 1 + S [ 2 ] ∗ B a s e r − 2 + . . . + S [ r ] ) ~~~~~~F(r)=(S[1]*Base^{r-1}+S[2]*Base^{r-2}+...+S[r]) F(r)=(S[1]∗Baser−1+S[2]∗Baser−2+...+S[r])% Mod
H ( s [ l , r ] ) = F ( r ) − F ( l − 1 ) ∗ B a s e r − l + 1 H(s[l,r])=F(r)-F(l-1)*Base^{r-l+1} H(s[l,r])=F(r)−F(l−1)∗Baser−l+1