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
#include
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;
		if(!find(a)) tot++;
		get(a);
	}
	printf("%d\n",tot);
	return 0;
}

挂链表法:

#include
#include
#include
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;i0){
			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;i0){//不断指向后面 
		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\)快多了