1. 哈希
1.1. 功能
存在性判断,判断是否相等
1.2. 原理
1.2.1. 哈希函数
把大范围的一组数
A
i
A_i
Ai映射到
B
i
B_i
Bi
每个
A
i
A_i
Ai有且只有一个对应的
B
i
B_i
Bi
一个
B
i
B_i
Bi可能对应多个
A
i
A_i
Ai(哈希碰撞)
常用哈希函数:
f
(
x
)
=
x
f(x)=x
f(x)=x
f
(
x
)
=
a
b
s
(
x
)
m
o
d
M
f(x)=abs(x) \bmod M
f(x)=abs(x)modM
f
(
x
)
=
(
x
m
o
d
M
+
M
)
m
o
d
M
f(x)=(x \bmod M +M) \bmod M
f(x)=(xmodM+M)modM
f
(
x
)
=
x
&
M
f(x)=x \& M
f(x)=x&M,其中M=0xfffff
1.2.1.1. 字符串哈希
1.2.1.1.1. 功能:
将字符串
S
S
S映射到整数
B
B
B,
通过判断B是否相等,判断S是否相等
1.2.1.1.2. 原理
把字符串的每项看做数位,
每个字符对应一个数字,
则字符串可以看作一个
P
P
P进制的数。
字符串哈希值
=
∑
i
=
0
l
e
n
(
S
)
−
1
S
i
×
P
i
=\sum_{i=0}^{len(S)-1}S_i \times P^i
=∑i=0len(S)−1Si×Pi
P=10
s="abccd"
h=12334
为了存入int64_t
,每次运算需要取余数。
或使用uint64_t
,自动取模于
2
64
2^{64}
264(自然溢出)。
为减少哈希冲突,
P
,
M
P,M
P,M通常为质数,常用
P
1
=
131
,
P
2
=
137
,
M
1
=
1
0
9
+
7
,
M
2
=
1
0
9
+
9
P_1=131,P_2=137,M_1=10^9+7,M_2=10^9+9
P1=131,P2=137,M1=109+7,M2=109+9
也可同时用多组
P
,
M
P,M
P,M(双哈希)
取模后,若
哈希
(
S
1
)
=
哈希
(
S
2
)
哈希(S_1)=哈希(S_2)
哈希(S1)=哈希(S2),
则极大概率有
S
1
=
S
2
S_1=S_2
S1=S2
若要获取子串哈希值,可使用前缀和。
涉及字符串反转后相等(如回文串问题),
同时使用前缀和、后缀和。
P=10,s="abccd"
h[1]=1
h[2]=12
h[3]=123
h[4]=1233
h[5]=12334
s(3~4)="cc"
h(3~4)=1233-12*10**2=33
h(i~j)=h[j]-h[i-1]*P**(j-i+1)
1.2.1.1.3. 实现
const int64_t P=131,M=1e9+7;
获取字符串哈希
int64_t strHash(const std::string s){
int64_t h=0;
for(int i=0;i<s.size();i++){
h=(h*P%M+(s[i]-'a'+1))%M;
}
return h;
}
前缀和实现 O ( 1 ) O(1) O(1)获取子串哈希
std::string s;//下标从1开始
int n;//s的长度
int64_t h[N]/*前缀和数组*/;
int64_t p[N]/*P的i次方*/;
//N为字符串长度
void init(){
p[0]=1;
for(int i=1;i<=n;i++){
h[i]=(h[i-1]*P%M+(s[i]-'a'+1))%M;
p[i]=p[i-1]*P%M;
}
}
int64_t subHash(int l,int r){
return ((h[r]-h[l-1]*p[r-l+1]%M)%M+M)%M;
}
1.2.2. 哈希表
为避免哈希碰撞导致错误判断,
将
A
i
A_i
Ai存入
M
+
1
M+1
M+1个线性表(常用链表)中,
用
B
i
B_i
Bi做线性表组的下标(哈希函数需要保证
B
i
<
M
B_i<M
Bi<M)
空间复杂度: O ( M + l e n ( A ) ) O(M+len(A)) O(M+len(A))
1.3. 操作
1.3.1. 插入元素
h[f(x)].insert(x)
时间复杂度取决于线性表的插入时间复杂度
若用链表实现则为
O
(
1
)
O(1)
O(1)
若用std::vecter实现则近似
O
(
1
)
O(1)
O(1),实际大于
O
(
1
)
O(1)
O(1)
1.3.2. 查询元素
h[f(x)].find(x)
设h[f(x)]
中有
k
k
k个元素,
则时间复杂度为
O
(
k
)
O(k)
O(k),
近似为
O
(
1
)
O(1)
O(1)
1.4. 实现
#define K 0x3fffff
struct S{
int64_t v;
int nxt;
};
S h[(int)1e7];//len(B)
int e=1;//结尾标记,0表示nullptr,不存数据
int hd[K+1];
bool find(int64_t x){
for(int i=hd[x&K];i;i=h[i].nxt){
if(h[i].v==x)return 1;
}
return 0;
}
void insert(int64_t x){
if(find(x))return;
h[e]=S{x,hd[x&K]};
hd[x&K]=e++;
}