哈希

https://zhuanlan.zhihu.com/p/104346215
本命素数:\(\mathtt{202099}\)\(\mathtt{1145142007}\)\(\mathtt{11451419198101979}\)

1 生日悖论

在认为完全随机的情况下,我们需要会分析冲突的概率。

我们需要给 \(n\) 个位置每一个位置分配一个 \([1, V]\) 的数,使得没有两个数相等。生日悖论的内容是,这样做的成功率是:\(\cfrac{V}{V} \times \cfrac{V-1}{V} \times ... \times \cfrac{V-n}{V}\)

因为 \(n = 3 \times 10^5\),如果 \(V = 10^9\),那么几乎不可能成功。因此设置 \(V = 10^{18}\) 是必要的。

2 一些好的哈希方式

令哈希方式有强随机性是很重要的。

2.1 字符串哈希

千万千万千万不能自然溢出,一定会被卡。其他的符合生日悖论的概率就好了。

允许 \(k\) 次失配的字符串匹配

问题:给定长为 \(n\) 的源串 \(s\),以及长度为 \(m\) 的模式串 \(p\),要求查找源串中有多少子串与模式串匹配。\(s'\)\(s\) 匹配,当且仅当 \(s'\)\(s\) 长度相同,且最多有 \(k\) 个位置字符不同。其中 \(1 \le n,m \le 10^6\)\(0 \le k \le 5\)

这道题无法使用 KMP 解决,但是可以通过哈希 + 二分来解决。

枚举所有可能匹配的子串,假设现在枚举的子串为 \(s'\),通过哈希 + 二分可以快速找到 \(s'\)\(p\) 第一个不同的位置。之后将 \(s'\)\(p\) 在这个失配位置及之前的部分删除掉,继续查找下一个失配位置。这样的过程最多发生 \(k\) 次。

总的时间复杂度为 \(O(m+kn \log_2 m)\)

2.2 sum hash 和 xor hash

这些 hash 的目的其实就是把某一些观测的现象(是否满足某个特定结构)用一个数字表示,满足这些现象到达一些指定状态的时候数字是我们可以预见的,方便维护。

一些方法:我们如果要求某一个东西出现偶数次,那么出现一次的时候异或上 \(x\)

对于一个颜色序列,我们维护双指针 \([l, r]\),想要维护序列中是否满足:每一种颜色要么不在 \([l,r]\) 中出现,要么在所有出现位置都在 \([l, r]\) 中。我们可以考虑对前面的出现位置赋随机权值,然后令最后一个位置的权值为之前权值之和的相反数。然后维护 sum 即可。

CSP2022 T3

快速维护状态是否为指定状态,可以给每个元素指定随机哈希值,然后加和。

CF1771F

【题意】给定序列 \(a\)\(m\) 次询问 \([l,r]\) 求出区间内最小的出现奇数次的数。

【分析】考虑异或处理奇数和偶数次,随机给每个数赋值一个数,(mt19937_64是64位整数生成器)然后每次询问的冲突概率为 \(1/2^{64}\)。主席树上二分。

2.3 树哈希

https://peehs-moorhsum.blog.uoj.ac/blog/7891

考虑这样一种哈希方式。对于一棵以 \(a\) 为根的子树,假设儿子是 \(v_1,v_2,⋯,v_k\),定义子树的哈希 \(h(a)=1+∑ \limits_{1≤i≤k}f(h(v_i))\)。其中\(h(v_i)\)\(v_i\) 对应子树的哈希,\(f\) 为一个待定函数。

可以证明:如果 \(f\) 为随机函数,这样的哈希在自然溢出下的期望冲突数不超过 \(O(n^2/2^w)\)。只需考虑最深的一对冲突点即可。

上述哈希最大的优势是好写。如果需要换根,第二次 dp 时只需把子树哈希减掉即可。(也就是随机 \(f\),再加起来加 \(1\)

实践中,我们并不能取一个真正的随机函数当 \(f\)。但事实上,没有特殊性质的 \(f\) 几乎都卡不掉;因为随便找个 \(f\) 大概率很随机。

注:可以把 \(f\) 看成一个 mt19937,但是这个比较慢,手写一个随机函数会比较好。

upd: 有一种 f 不会被卡掉,是 xorshift。

认准唯一真写法。

\(h(i)=\sum \limits_{j \in son_i} f(h(j)) + c\)

\(f(x)\) 使用 xorshift 的方法生成:

ull func(ull x) {
    x^=(x<<7); x^=(x>>11); x^=(x<<13);
    return x;
}
posted @ 2022-10-25 21:09  OIer某罗  阅读(158)  评论(0编辑  收藏  举报