模拟哈希表
引入
哈希表就是根据一个关键值key进行高效访问的数据结构,可以通过哈希函数把一个数据当做key进行映射得到一个储存地址从而进行访问。
比如想要查询100个数字范围在(1 ~ 1e8),查询它们是否有重复的值,那么就可以用哈希表来解决这个问题。
对于小的数字我们就会习惯的会去一个数组进行标记,但是对于大的数字,开一个很大数组用来标记是很浪费空间,而且要处理的数只有100个,开一个1e8的数组实在是划不来,而且当数据再增大时,对于C++可能就无法直接开出这样的数组。
哈希函数
此时就可以对此些数字进行转换,比如,我们可以对其进行取模,这样就可以把他们都转化成一定范围的数字。
这种把这些数据按照一定方式进行转化的函数就称之为哈希函数,上面所使用的哈希函数就是取模。
处理冲突
可以发现用哈希函数处理之后,可能存在相同的值,比如5 % 3 = 2, 8 % 3 = 2这样就可能会使查询的结果错误,那么就需要用一种方式处理冲突了(可以证明当模数取质数时冲突几率最小)。
拉链法
拉链法处理冲突很简单,对于几个重复的值可以把他们放在一个链表里,查询时只需要去翻查链表就可以知道是否存在了
using namespace std;
const int mod = 100003;
int h[100005],cnt = 1,n;
struct node{
int nex,number;
}edge[100005];//链表
void insert(int x){
int hashCode = (x % mod + mod) % mod;//把负数也给映射过来
edge[cnt].number = x;
edge[cnt].nex = h[hashCode];
h[hashCode] = cnt++;
}
bool find(int x){
int hashCode = (x % mod + mod) % mod;
for(int i = h[hashCode]; i != 0;i = edge[i].nex){//遍历该哈希值下的所有数
if(edge[i].number == x)return true;
}
return false;
}
字符串哈希
字符串的哈希可以采用前缀哈希(在竞赛中)
对于一个字符串可以把它当成一个p进制的数字,比如用sum来表示前缀对于字符串:“acf”
那么就有:
s
u
m
[
1
]
=
P
∗
‘
a
’
sum[1] = P * ‘a’
sum[1]=P∗‘a’
s
u
m
[
2
]
=
P
2
∗
‘
c
’
+
s
u
m
[
1
]
sum[2] = P^2 * ‘c’ + sum[1]
sum[2]=P2∗‘c’+sum[1]
s
u
m
[
3
]
=
P
3
∗
‘
f
’
+
s
u
m
[
2
]
sum[3] = P^3 * ‘f’ + sum[2]
sum[3]=P3∗‘f’+sum[2]
但是这样就会有一个问题,最后求出来的数字很大,所以就需要进行取模。然后就可以完成前缀hash了。
对于P一般取131,对于模数一般取
2
64
2^{64}
264这样开unsigned long long进位时就会自动取模了,对于重复的字符串,重复的几率很小,所以几乎可以当做不重复,如果相求某一段字符串的哈希值那么就可以用
s
u
m
[
r
]
−
s
u
m
[
l
−
1
]
∗
P
r
−
l
+
1
sum[r] - sum[l - 1] * P^{r - l + 1}
sum[r]−sum[l−1]∗Pr−l+1这个公式求得
题目: 字符串hash
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define eps 1e-8
using namespace std;
const ll maxn = 1e5 + 5;
typedef unsigned long long ull;
int n,m,L1,R1,L2,R2;
ull p[maxn],sum[maxn];
char str[100005];
ull finds(int l,int r){
ull res = sum[r] - sum[l - 1] * p[r - l + 1];
return res;
}
int main(){
scanf("%d %d",&n,&m);
scanf("%s",str + 1);
p[0] = 1;
for(int i = 1; i <= n; i++){
p[i] = p[i - 1] * 131;
sum[i] = sum[i - 1] * 131 + str[i];
}
while(m--){
scanf("%d %d %d %d",&L1,&R1,&L2,&R2);
if(finds(L1,R1) == finds(L2,R2))
printf("Yes\n");
else
printf("No\n");
}
return 0;
}