哈希表( H a s h Hash Hash表、散列表)
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。
模板题链接:AcWing 840.模拟散列表
拉链法(用个链表来存储每个位置上的集合)
AC核心代码:要记得把h数组全部赋值为-1。这里N为大于100000的第一个质数,用质数来说相对比较好,能最大限度减少冲突
const int N = 1e5 + 3;
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(x == e[i])
return true;
}
return false;
}
开放寻址法
核心就是先找个一个位置,如果这个位置上有数就往看下一个位置,直到找到没得数的位置。一般把数组开成原来的2-3倍大小。同样也要把N设置为第一个大于的质数,减少冲突
注意这道题不能把
h
h
h 数组赋值为全
0
0
0 ,因为
0
0
0 可能在数据集里面,应该全部赋值为一个永远用不到的数,比如
n
u
l
l
=
0
x
3
f
3
f
3
f
3
f
null=0x3f3f3f3f
null=0x3f3f3f3f
AC核心代码如下:
const int N = 2e5+3,null=0x3f3f3f3f;
int h[N];
void insert(int x)
{
int k = (x % N + N) % N;
while(true)
{
if(h[k]==null)
{
h[k] = x;
break;
}
++k;
}
}
bool find(int x)
{
int k = (x % N + N) % N;
while(h[k]!=null)
{
if(h[k]==x)
return true;
++k;
}
return false;
}
字符串哈希(这里介绍的是字符串前缀哈希)
对于一个字符串:"
A
B
C
A
B
C
D
E
X
A
C
W
I
N
G
ABCABCDEXACWING
ABCABCDEXACWING"
有前缀哈希数组
h
h
h,则有
h
[
0
]
=
0
h[0]=0
h[0]=0
h
[
1
]
=
h[1]=
h[1]=“
A
A
A” 的哈希值
h
[
2
]
=
h[2]=
h[2]=“
A
B
AB
AB” 的哈希值
h
[
3
]
=
h[3]=
h[3]=“
A
B
C
ABC
ABC” 的哈希值
h
[
4
]
=
h[4]=
h[4]=“
A
B
C
A
ABCA
ABCA” 的哈希值
⋅
⋅
⋅
⋅
⋅
⋅
······
⋅⋅⋅⋅⋅⋅
这里的
h
h
h 数组存储的是这个字符串的哈希值
比如"
A
B
C
D
ABCD
ABCD"这个字符串,看成是
p
p
p进制的数
A
B
C
D
A B C D
ABCD
(
1234
)
P
( 1 2 3 4)_P
(1234)P
=
=
=
(
1
∗
p
3
+
2
∗
p
2
+
3
∗
p
1
+
4
∗
p
0
)
%
Q
(1*p^3+2*p^2+3*p^1+4*p^0)\%Q
(1∗p3+2∗p2+3∗p1+4∗p0)%Q
这样就把任何一个字符串映射成了
[
0
,
Q
−
1
]
[0,Q-1]
[0,Q−1]之间的一个数
P
S
:
PS:
PS:
一般不能把一个字母映射成0,比如上面把A映射成0,那么ABCD和BCD的哈希值都是一样的了
经验值:当
P
=
131
P=131
P=131或者
P
=
13331
P=13331
P=13331,
Q
=
2
64
,
一
般
的
99.99
%
的
情
况
下
不
会
出
现
冲
突
,
冲
突
的
概
率
大
约
是
几
十
亿
分
之
一
Q=2^{64},一般的99.99\%的情况下不会出现冲突,冲突的概率大约是几十亿分之一
Q=264,一般的99.99%的情况下不会出现冲突,冲突的概率大约是几十亿分之一
模板题:AcWing 841.字符串哈希
这里我们直接用ULL,因为溢出相当于取模
可以先预处理p数组,之后查询的时候就非常快:
p
[
i
]
=
p
[
i
−
1
]
∗
P
p[i]=p[i-1]*P
p[i]=p[i−1]∗P
预处理
h
h
h 哈希数组:
a
r
r
[
i
]
=
a
r
r
[
i
−
1
]
∗
p
+
s
t
r
[
i
]
arr[i]=arr[i-1]*p+str[i]
arr[i]=arr[i−1]∗p+str[i],
其
中
s
t
r
[
i
]
只
要
不
为
0
就
行
其中str[i]只要不为 0 就行
其中str[i]只要不为0就行
得到一个字符串中的
[
l
,
r
]
[l,r]
[l,r]段子字符串的哈希值是:
a
r
r
[
r
]
−
a
r
r
[
l
−
1
]
∗
p
[
r
−
l
+
1
]
arr[r]-arr[l-1]*p[r-l+1]
arr[r]−arr[l−1]∗p[r−l+1]
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
const int MAXN = 1e5 + 10;
int P = 131;
ULL arr[MAXN], p[MAXN];
int n, m, l1, r1, l2, r2;
char str[MAXN];
ULL get(int l, int r)
{
return arr[r] - arr[l - 1] * p[r - l + 1];
}
int main()
{
p[0] = 1;
scanf("%d%d%s", &n, &m, str + 1);
for(int i = 1; i <= n; ++i)
{
p[i] = p[i - 1] * P;
arr[i] = arr[i - 1] * P + str[i];
}
while(m--)
{
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
if(get(l1, r1) == get(l2, r2))
printf("Yes\n");
else
printf("No\n");
}
return 0;
}