哈希算法入门
序列哈希(字符串哈希)
概念
序列哈希是用于判断两个字符串/数列是否相等的工具
对于字符串\(S\),他的哈希计算公式为
\(hash(S)=\sum_{i=1}^{|S|}S[i]\times base^{i}\)
但是由于涉及指数运算与乘积,容易造成溢出.所以我们一般需要取模
\(hash(S)=(\sum_{i=1}^{|S|}S[i]\times base^{i}) \bmod p\)
序列哈希(字符串哈希)的特点在于
-
有序性(同样字符构成的不同排列通常会产生不同的哈希值)
-
方便取区间哈希值
常见计算方法
为了方便取字串的哈希值,我们通常构造哈希前缀和 \(h\)
\(h[x]=(h[x-1]\times base+S[x])\bmod p\)
展开可得
\(h[x]=(\sum_{i=1}^{x}S[i]\times base^{x-i})\bmod p\)
同时我们预处理\(base\)的幂
\(pow[x]=base^x\bmod p\)
对于\(S\)的区间\([l,r]\)的哈希计算
\(hash(S_{[l,r]})=(\sum_{i=l}^{r}S[i]\times base^{r-i})\bmod p\)
等价于
\(hash(S_{[l,r]})=(h[r]-h[l-1]\times pow[r-l+1])\bmod p\)
这样我们就可以\(O(n)\)预处理\(O(1)\)求出任意字串的哈希值了
这种方法不需要使用逆元,因此在竞赛中更加常用
子集哈希
子集哈希主要用于判断两个集合是否相等,或者快速判断两个集合之间的关系
对于每个可能出现的元素\(x\),我们随机分配一个哈希值\(val[x]\)
对于一个集合\(S\),定义
\(hash(S)=\sum_{x\in S}val[x]\)
为了防止数值过大,通常需要取模
\(hash(S)=(\sum_{x\in S}val[x])\bmod p\)
子集哈希的特点在于
-
无序性(集合中元素的排列顺序不会影响哈希值)
-
可以方便地进行集合的合并与删除
-
可以用于快速判断两个集合是否相等
例如有两个集合\(A,B\),如果
\(hash(A)=hash(B)\)
则认为\(A=B\)
对于集合的并集,如果\(A,B\)没有重复元素,则有
\(hash(A\cup B)=hash(A)+hash(B)\)
对于删除元素\(x\),可以直接进行
\(hash(S\setminus{x})=hash(S)-val[x]\)
因此子集哈希特别适合处理集合的增删以及集合相等性判断
不过需要注意,子集哈希同样存在哈希碰撞,因此\(hash(A)=hash(B)\)并不能严格证明\(A=B\)
指数哈希
指数哈希可以看作子集哈希的一种特殊形式,主要用于判断两个集合是否相等
对于每个元素\(x\),我们使用\(base^x\)作为其哈希值
对于一个集合\(S\),定义
\(hash(S)=\sum_{x\in S}base^x\)
为了防止数值过大,通常需要取模
\(hash(S)=(\sum_{x\in S}base^x)\bmod p\)
指数哈希的特点在于
-
无序性(集合中元素的排列顺序不会影响哈希值)
-
不同元素具有不同的权值
-
可以方便地进行集合的合并与删除
例如有两个集合\(A,B\),如果
\(hash(A)=hash(B)\)
则认为\(A=B\)
对于加入元素\(x\),可以进行
\(hash(S\cup{x})=hash(S)+base^x\)
对于删除元素\(x\),可以进行
\(hash(S\setminus{x})=hash(S)-base^x\)
因此指数哈希特别适合处理集合相等性判断以及集合的增删
指数哈希本质上仍然属于子集哈希,只不过将元素\(x\)的哈希值固定为\(base^x\)
需要注意,指数哈希同样存在哈希碰撞,因此\(hash(A)=hash(B)\)并不能严格证明\(A=B\)
异或哈希
异或哈希与子集哈希类似,也是通过给每个元素分配一个随机值来表示一个集合
对于每个元素\(x\),随机生成一个哈希值\(val[x]\)
对于集合\(S\),定义
\(hash(S)=\bigoplus_{x\in S}val[x]\)
其中\(\oplus\)表示按位异或
异或哈希的特点在于
-
无序性(元素的排列顺序不会影响哈希值)
-
可以方便地进行集合的添加与删除
-
不需要取模,因为异或本身不会产生传统意义上的溢出问题
利用异或的性质
\(x\oplus x=0\)
\(x\oplus0=x\)
\(x\oplus y\oplus y=x\)
因此对于集合\(S\),加入元素\(x\)时
\(hash(S\cup{x})=hash(S)\oplus val[x]\)
删除元素\(x\)时同样可以直接进行
\(hash(S\setminus{x})=hash(S)\oplus val[x]\)
这是因为
\(val[x]\oplus val[x]=0\)
例如有两个集合\(A,B\),可以通过
\(hash(A)\oplus hash(B)\)
得到两个集合中出现次数不同的元素对应的哈希值
如果\(A,B\)完全相同,则
\(hash(A)\oplus hash(B)=0\)
三种哈希的区别
序列哈希主要用于处理有序序列
\(hash(S)=\sum S[i]\times base^{i}\)
元素的位置会影响哈希值
子集哈希主要用于处理无序集合
\(hash(S)=\sum_{x\in S}val[x]\)
元素的顺序不会影响哈希值
指数哈希同样用于处理无序集合
\(hash(S)=\sum_{x\in S}base^x\)
可以看作子集哈希的一种特殊形式
异或哈希同样主要用于处理无序集合
\(hash(S)=\bigoplus_{x\in S}val[x]\)
但是异或哈希利用了异或的性质,可以非常方便地进行元素的加入和删除
简单来说
-
序列哈希:关注顺序
-
子集哈希:不关注顺序,通过加法维护
-
指数哈希:不关注顺序,使用\(base^x\)作为元素权值
-
异或哈希:不关注顺序,通过异或维护

浙公网安备 33010602011771号