Hash表
存储结构
- 开放寻址法
- 拉链法
将一些比较复杂的数据映射到0-N这些数,比如将0 - 109映射到0 - 105
将输入x (-10^9 ~ + 10^9)通过哈希函数转换为输出y (0 ~ +10^5)
问题:
- 哈希函数一般怎么写? x mod 10^5 一般来说模的数要取成质数,冲突的概率会小一点
- 有冲突怎么办?即不同的输入x导致了相同的输出y
拉链法:
开一个一维数组来存储所有的哈希值,如果有出现冲突的话就在下面把数拉出来
数组a[] : 1 2 3 4 5 6
|
23
|
13
开放寻址法:
开一个一维数组,开到数据范围的2-3倍
一般哈希算法模板:
拉链法
#include <cstring>
#include <iostream>
// N一般为质数,比如1e5 + 7
// h[]是一条主链,e[]是主链下面拖的一条链
// e[]存放值,ne[]存放指针,idx表示用到了哪些位置
int h[N], e[N], ne[N], idx;
// 向哈希表中插入一个数
void insert(int x)
{
int k = (x % mod + mod) % mod;
// k是存放在h中的位置
e[idx] = x;
ne[idx] = h[k];
// h[k]存放的是下面拖的第一个点的下标(类似于头指针)
// 让新的点的next指针指向h[k],再让h[k]指向新的点
h[k] = idx ++;
}
// 在哈希表中查询某个数是否存在
bool query(int x)
{
int k = (x % mod + mod) % mod;
for (int i = h[k]; i != -1; i = ne[i])
if (e[i] == x)
return true;
return false;
}
// 链表头最开始初始化为-1
memset(h, -1, sizeof(h));
开放寻址法
const int null = 0x3f3f3f3f;
int h[N]; // 一般开到2-3倍
// 质数一般取刚好比N大的质数
// 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
int query(int x)
{
int t = (x % mod + mod) % mod;
while (h[t] != null && h[t] != x)
{
t ++ ;
if (t == N) t = 0;
}
return t;
}
memset(h, 0x3f, sizeof(h));
// memset按照字节去分配,而一个int为4字节,所以将0x3f3f3f3f拆分成0x3f
int k = query(x);
// 插入
h[k] = x;
// 查找
if(h[k] != null) cout << "Yes" << endl;
else cout << "No" << endl;
字符串哈希
字符串前缀哈希法
Str = “ABCABCDEYXCAcwing”
预处理所有前缀的哈希
h[0] = 0
h[1] = "A"的哈希值
h[2] = "AB"的哈希值
-
把字符串看成是p进制的数
ABCD
(1234)p
哈希值 = (1 * p^3 + 2 * p^2 + 3 * p^1 + 4 * p^0 ) mod q
-
不能映射成0
-
此处假定不存在冲突
可以利用前缀哈希求出所有子串的哈希值
计算子串 str[l ~ r] 的哈希值:
1 l-1 l r
r-1 l l-1 0
h[1 - r] = h[r]
h[1 - l-1] = h[l-1] * p^(r-l+1)
从 l l l到 r r r这段的哈希值 h [ R ] − h [ L − 1 ] × p R − L + 1 h[R]-h[L-1]\times p^{R-L+1} h[R]−h[L−1]×pR−L+1
用于判断两个子串是否相同
字符串哈希算法模板:
// 核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
// 小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果
typedef unsigned long long ULL;
const int P = 13331, N = 1e5 + 10;
// const ULL q = pow(2,64);
ULL h[N], p[N]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
string str;
str = " " + str;
// 初始化
p[0] = 1;
for (int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * P + str[i]; // 计算前h位的哈希值
p[i] = p[i - 1] * P; // 计算p的指数次方
}
// 计算子串 str[l ~ r] 的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}