luogu P3370 【模板】字符串哈希
题面传送门
不想另外写\(hash\)题解了,就拿这一篇的解题报告代替吧。
哈希(顾名思义),是由英文\(hash\)音译过来的,主要用途是将数值很大的数存下来并在调用时实现数组下标查询,一般用一个哈希函数来实现,主要方法有\(\%\)运算。
哈希冲突:定义\(x\neq y\)但\(hash(x)=hash(y)\),即为哈希冲突,解决方法有几种:开放寻址法,挂链表法,多重\(hash\),其中开放寻址法,挂链表法是用时间换正确,多重\(hash\)则只是常数略大。
实现方式(以字符串\(hash\)为例):
先是类似于状压的东西,确定一个进制\(v\),保证\(v\)不小于字符串内不同字符数,将字符串通过\(v\)进制转化成一个十进制数。
以上是字符串\(hash\)特殊的步骤。
然后将这个十进制数扔到\(hash()\)里面去,得到一个\(hash\)值设为\(tmp\)。
接着就有两种做法:
1.开放寻址法::设两个数组,一个是\(f\)一个是\(g\),\(f\)数组表示存在这里的原数,\(g\)数组表示存在这里的个数。
修改由\(f_{tmp}\)找起,若等于原数,则\(f[_{tmp}++\),表示这个位置上多了一个数。若到了\(f_tmp=0\)则新建一个地址,存下这个数。
查询:由\(f_tmp\)找起,若等于原数,则答案为\(g_{tmp}\),表示这个大数以前出现过\(g_{tmp}\)次。若到了\(f_{tmp}\),则答案为\(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);
}
\(hash\)还是很有用的,至少比\(map\)快多了