将大数字哈希
将某个数x哈希 x mod N(N是一个质数)
如果x足够大,很可能会发生冲突,按照冲突的解决方法分为两种——拉链法, 开放寻址法
例题:acwing840. 模拟散列表
拉链法
将发生冲突的数用一个单链表进行存储,多个单链表构成一个邻接表,也就是用邻接表来解决冲突
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100003; //大于给定数据范围的一个质数
int h[N], e[N], ne[N], idx;
//将x插入哈希表
void insert(int x)
{
int k = (x % N + N) % N; //将x进行哈希映射,保证k是正数
e[idx] = x;
ne[idx] = h[k];
h[k] = idx ++;
}
//寻找x在哈希表中是否存在
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 main()
{
memset(h, -1, sizeof h); //将哈希表的每个头节点指向-1,表示空
int n;
cin >> n;
while(n -- )
{
int x;
char c;
cin >> c >> x;
if(c == 'I') insert(x);
else
if(find(x)) puts("Yes");
else puts("No");
}
}
开放寻址法
开一个一维的数组,不过数组长度得是给定数据范围的2~3倍(经验值,这样处理可以解决掉冲突),若是发生了冲突,就往后寻找空位
#include <iostream>
#include <cstring>
using namespace std;
//用null表示该处没被用过
const int N = 200003, null = 0x3f3f3f3f;
int h[N];
//找到应该插入x的下标
int find(int x)
{
int k = (x % N + N) % N;
//若h[k] == null 说明,该点没被用过,h[k] == x表是该点已经存在
while(h[k] != null && h[k] != x)
{
k ++; //有冲突就往后走
if(k == N) k = 0; //走到数组的最后了就从头重新开始
}
return k;
}
int main()
{
memset(h, 0x3f, sizeof h);
int n;
cin >> n;
while(n -- )
{
int x;
char c;
cin >> c >> x;
int k = find(x);
if(c == 'I') h[k] = x;
else
{
if(h[k] != null) puts("Yes");
else puts("No");
}
}
return 0;
}
字符串哈希
将字符串看成p是一个p进制的数,预处理出来字符串前缀的前缀和
P进制一般取131或13331(经验值,冲突可能性较小),人品足够好,不存在冲突
将预处理出来的字符串mod 264,用unsigned long long 存储刚刚好,当数据较大时,数据会溢出,剩下的数据刚好就是mod 264后的数
#include <iostream>
using namespace std;
typedef unsigned long long ULL;
const int N = 1e5 + 10, P = 131;
char s[N];
ULL p[N], h[N];
//求出来从l到r对应的字符串的哈希值
ULL get(int l, int r)
{
return h[r] - h[l - 1] * p[r - l + 1];
}
int main()
{
int n, m;
cin >> n >> m >> s + 1;
p[0] = 1;
for(int i = 1; i <= n; i ++ )
{
h[i] = h[i - 1] * P + s[i]; //预处理字符串的前缀
p[i] = p[i - 1] * P; //用来计算前缀之间的差值所对应的p进制倍数
}
while(m -- )
{
int l1, r1, l2, r2;
cin >> l1 >> r1 >> l2 >> r2;
if(get(l1, r1) == get(l2, r2)) puts("Yes");
else puts("No");
}
return 0;
}