Hash
Hash
一种快速判定的方法,具体是将一个复杂的结构映射成一个整数,用极低的错误概率换取极快的比较效率。
进制Hash
对于序列的Hash,关心元素之间的位置关系。
不要把任意字符对应到数字0,比如假如把a对应到数字0,那么将不能只从Hash结果上区分ab和b.
注意有时候卡自然溢出和 int 范围内的模数,可以使用 \(10^{18}+3\) 。
异或Hash
随机赋权,针对于集合的Hash,可以实现出现一个数时加入,再次出现时删除。
应用一:判断一个区间是否与给定的一些区间存在交叉
从端点的角度考虑,假设给定了一些区间 \([L_i,R_i]\)。
对于每个 \([L_i,R_i]\) ,使用异或Hash,随机一个权值 \(v\),\(q_L\oplus v\to q_L,q_{R+1}\oplus v\to q_{R+1}\),最后令 \(V\) 表示 \(q\) 的前缀异或和。
那么另一个区间 \([l,r]\) 没有与任何一个 \([L_i,R_i]\) 交叉的充要条件为 \(V_l=V_r\)
应用二:判断两个点“环境”是否相同。
比如:给定 \(n\) 个区间,那么一个点被那些区间包含就是一个“环境”。两个点严格被相同的区间包含,就称作两个点环境相同。
对于这个问题,和应用一类似,给每个区间赋一个权值,分别打在左闭右开端点上,求前缀异或和。
如果两个位置的值相同,就认为这两个点“环境”相同。
加和Hash
针对于可重集的Hash,\(+w_x\) 即向集合加入一个 \(x\) ,\(-w_x\) 即从集合中删除一个 \(x\)。
记一种随机权值Hash的快速查表方法
对于 for(int i=1;i<=n;i++)w[i]=rnd();如果我们要查询一个 \(W\) 在不在 \(\{w\}\) 里,或者进一步的想知道对应的下标是什么,有以下方法。
朴素想法:unordered_map,就是之间建立反过来的映射,这样常数太大。
手写Hash表:即取 \(w\) 后若干位,将相同的放入一个vector每次遍历查找,由于 \(w\) 是随机的,期望大小为 \(\mathcal O(1)\)。
进阶想法:直接将 \(w_i\) 的二进制下的后__lg(n)位变成 \(i\),这样遇到一个数 \(W\),取出后__lg(n)位,设作 \(x\),如果 \(x\in [1,n]\land w_x=W\),则 \(W\) 存在。

浙公网安备 33010602011771号