哈希表
- 一般哈希
拉链法模板
int h[N], e[N], ne[N], idx;
// 向哈希表中插入一个数
void insert(int x)
{
int k = (x % N + N) % N;
e[idx] = x;
ne[idx] = h[k];
h[k] = idx ++ ;
}
// 在哈希表中查询某个数是否存在
bool find(int x)
{
int k = (x % N + N) % N;
for (int i = h[k]; i != -1; i = ne[i])
if (e[i] == x)
return true;
return false;
}
开放寻址法模板
int h[N];
// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
int find(int x)
{
int t = (x % N + N) % N;
while (h[t] != null && h[t] != x)
{
t ++ ;
if (t == N) t = 0;
}
return t;
}
- 字符串哈希
Step 1. 预处理出所有字符串前缀的哈希值: h[ i ] = h[i - 1] × \times × P + str[ i ]
Step 2. 求任一子串的哈希值:h[ L~R ] = h[ R ] - h[ L - 1] × \times × P R+L-1
如何定义某个前缀的哈希值?
①把字符窜看成一个P进制的数,将这个数转化为十进制数;②最后将这个数mod Q,即可把所有子串映射到 0 ~ Q-1
Tips:
①不要把字符映射为0 (eg.把A的值映射为0,则"AAAAA"所有前缀哈希值都为0),ASCII码为0的字符是’\0’ (字符串结束标志)一般不会出现
②当P = 131 || P = 13331、Q = 2 64 ==> 哈希冲突概率最小
③用 unsigned long long 存储哈希表,溢出相当于对 2^64 取模,省略了手动运算(在C++中int溢出是undefined behavior,是否会取模取决于编译器。unsigned int溢出是define behavior,在溢出时会自动取模。)
字符串哈希模板
typedef unsigned long long ULL;
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * P + str[i];
p[i] = p[i - 1] * P;
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
- 二维哈希
二维哈希:从这篇博客学到的,真的写的很好捏
①首先, 按照一维哈希表预处理出每一行的哈希值:h[ i ] [ j ] 即第 i 行中(1, j)子串的哈希值 h[ i ] = h[i - 1] × \times × P1 + str[ i ] (P1 = 131)
然后,把每一行看作一个纵坐标上的点,于是又可以转换成以为哈希来处理 h[ i ][ j ] += h[i - 1][ j ] * P2 (P2 = 233)
③求(x1, y1)到(x2, y2)子矩阵的哈希值:ask(x1, y1, x2, y2) = h[x2][y2] - h[x2][y1-1] × \times ×p1[y2 - y1 + 1] - h[x1 - 1][y2] × \times ×p2[x2 - x1 + 1] - h[x1 - 1][y1 - 1] × \times ×p1[y2 - y1 + 1 ] × \times ×p2[x2 - x1 + 1]