一般哈希
一般哈希 —— 模板
//一般哈希
(1) 拉链法
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;
}
(2) 开放寻址法
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;
}
一般哈希 —— 模板题 AcWing 840. 模拟散列表
840. 模拟散列表
维护一个集合,支持如下几种操作:
- I x,插入一个数 x;
- Q x,询问数 x 是否在集合中出现过;
现在要进行 N 次操作,对于每个询问操作输出对应的结果。
输入格式
第一行包含整数 N,表示操作数量。
接下来 N 行,每行包含一个操作指令,操作指令为 I x,Q x 中的一种。
输出格式
对于每个询问指令 Q x,输出一个询问结果,如果 x 在集合中出现过,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤N≤105
−109≤x≤109
输入样例:
5 I 1 I 2 I 3 Q 2 Q 5
输出样例:
Yes No
代码:
- 解法1:拉链法
//一般哈希模拟题 #include<iostream> #include<cstring> using namespace std; const int N=1e5+10; //拉链法 int h[N],e[N],ne[N],idx; // 1 插数 void insert(int x){ int k=(x%N+N)%N; e[idx]=x; ne[idx]=h[k],h[k]=idx++; } // 2 检数 bool check(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(){ int n; cin>>n; memset(h,-1,sizeof h); while(n--){ string s; int x; cin>>s>>x; if(s=="I") insert(x); else if(check(x)) puts("Yes"); else puts("No"); } return 0; }
- 解法2:开放寻址法
//一般哈希模拟题 #include<iostream> #include<cstring> using namespace std; const int N=2e5+3; const int null=0x3f3f3f3f; //开放寻址法 int h[N]; 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; } int main(){ int n; cin>>n; memset(h,0x3f,sizeof h); while(n--){ string c; int x; cin>>c>>x; if(c=="I") h[find(x)]=x; else if(h[find(x)]!=null) puts("Yes"); else puts("No"); } return 0; }
字符串哈希
字符串哈希 —— 模板
/*
核心思想:将字符串看成 P进制数,P的经验值
是 131或 13331,取这两个值的冲突概率低
小技巧:取模的数用 2^64,这样
直接用 unsigned long long存储,
溢出的结果就是取模的结果
*/
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];
}
字符串哈希 —— 模板题 AcWing 841. 字符串哈希
841. 字符串哈希
给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1]和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n 和 m,表示字符串长度和询问次数。
第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m 行,每行包含四个整数 l1,r1,l2,r2,表示一次询问所涉及的两个区间。
注意,字符串的位置从 1 开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤n,m≤105
输入样例:
8 3 aabbaabb 1 3 5 7 1 3 6 8 1 2 1 2
输出样例:
Yes No Yes
代码:
//字符串哈希
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+10,P=131;
typedef unsigned long long ull;
ull h[N],p[N];
ull get(int l,int r){return h[r]-h[l-1]*p[r-l+1];}
int main(){
int n,m;
cin>>n>>m;
string str;
cin>>str;
p[0]=1;
for(int i=0;i<n;i++){
h[i+1]=h[i]*P+str[i];
p[i+1]=p[i]*P;
}
while(m--){
int l1,r1,l2,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(get(l1,r1)==get(l2,r2)) puts("Yes");
else puts("No");
}
return 0;
}