题面传送门
不想另外写
h
a
s
h
hash
hash题解了,就拿这一篇的解题报告代替吧。
哈希(顾名思义),是由英文
h
a
s
h
hash
hash音译过来的,主要用途是将数值很大的数存下来并在调用时实现数组下标查询,一般用一个哈希函数来实现,主要方法有
%
\%
%运算。
哈希冲突:定义
x
≠
y
x\neq y
x=y但
h
a
s
h
(
x
)
=
h
a
s
h
(
y
)
hash(x)=hash(y)
hash(x)=hash(y),即为哈希冲突,解决方法有几种:开放寻址法,挂链表法,多重
h
a
s
h
hash
hash,其中开放寻址法,挂链表法是用时间换正确,多重
h
a
s
h
hash
hash则只是常数略大。
实现方式(以字符串
h
a
s
h
hash
hash为例):
先是类似于状压的东西,确定一个进制
v
v
v,保证
v
v
v不小于字符串内不同字符数,将字符串通过
v
v
v进制转化成一个十进制数。
以上是字符串
h
a
s
h
hash
hash特殊的步骤。
然后将这个十进制数扔到
h
a
s
h
(
)
hash()
hash()里面去,得到一个
h
a
s
h
hash
hash值设为
t
m
p
tmp
tmp。
接着就有两种做法:
1.开放寻址法::设两个数组,一个是
f
f
f一个是
g
g
g,
f
f
f数组表示存在这里的原数,
g
g
g数组表示存在这里的个数。
修改由
f
t
m
p
f_{tmp}
ftmp找起,若等于原数,则
f
[
t
m
p
+
+
f[_{tmp}++
f[tmp++,表示这个位置上多了一个数。若到了
f
t
m
p
=
0
f_tmp=0
ftmp=0则新建一个地址,存下这个数。
查询:由
f
t
m
p
f_tmp
ftmp找起,若等于原数,则答案为
g
t
m
p
g_{tmp}
gtmp,表示这个大数以前出现过
g
t
m
p
g_{tmp}
gtmp次。若到了
f
t
m
p
f_{tmp}
ftmp,则答案为
0
0
0。
2.挂链表法:
挂链表法和图论中的邻接表差不多。只不过多存一个原数而已。
代码实现:
开放寻址法:
#include<cstdio>
#include<iostream>
using namespace std;
int n,tot,g[100039],ans;
string a,f[100039];
inline void get(string a){
register int i,tmp;ans=0;
for(i=0;i<a.size();i++) ans=(ans*139+a[i])%20039;//139进制转10进制,%20039 为hash函数
tmp=ans;
while(g[tmp]){//不断往后查找
if(f[tmp]==a) break;
tmp=(tmp+1)%20039;
}
f[tmp]=a;
g[tmp]++;
}
inline int find(string a){
register int i,tmp;ans=0;
for(i=0;i<a.size();i++) ans=(ans*139+a[i])%20039;
tmp=ans;
while(g[tmp]){//往后查询
if(f[tmp]==a) break;
tmp=(tmp+1)%20039;
}
return g[tmp];
}
int main(){
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++){
cin>>a;
if(!find(a)) tot++;
get(a);
}
printf("%d\n",tot);
return 0;
}
挂链表法:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,ans,h[100039],t[100039],head,z[100039],tot,g[100039];
string a,f[100039];
inline void get(string a){
register int i,tmp;ans=0;
for(i=0;i<a.size();i++) ans=(ans*139+a[i])%100039;
if(!h[ans]) g[++head]=1,h[ans]=t[ans]=head,f[head]=a;//如果没有头,新建一个
else {
tmp=h[ans];
while(tmp>0){
if(f[tmp]==a) break;
tmp=z[tmp];
}
if(f[tmp]==a) g[tmp]++;//如果相等
else g[++head]=1,z[t[ans]]=head,t[ans]=head,f[head]=a;//遇见空白
}
}
inline int find(string a){
register int i,tmp;ans=0;
for(i=0;i<a.size();i++) ans=(ans*139+a[i])%100039;
tmp=h[ans];
while(tmp>0){//不断指向后面
if(f[tmp]==a) break;
tmp=z[tmp];
}
return g[tmp];
}
int main(){
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++){
cin>>a;
if(!find(a)) tot++;
get(a);
}
printf("%d",tot);
}
h a s h hash hash还是很有用的,至少比 m a p map map快多了