luogu P3370 【模板】字符串哈希

题面传送门
不想另外写 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快多了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值