luogu P3370 【模板】字符串哈希算法

本文深入探讨了哈希算法在字符串比较中的应用,包括单重与双重哈希的实现方法,以及如何通过使用特定的素数来降低哈希冲突的概率。文章提供了具体的代码示例,并讨论了自然溢出哈希的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

嗯,以前一直没有好好看看字符串和哈希算法,实际上哈希算法的操作和运算在字符串得操作中是非常常见的,当然字典树来处理字符串也是非常常见的,这一篇就依着luogu的这道题当作给自己的一个总结吧

哈希算法我个人理解的话,我们在进行字符串比较的时候,我们可以通过某种加密的方式,将字符串化为一串数字,(一般而言,我们把a-z对应到数字1-26)使得相同得字符串得到的数字一定相同,使得不同的字符串的数字尽量不同,如果有相同的话我们称之为(哈希冲突),我们可以通过多种方法来降低这种不同字符串通过加密而导致结果相同的概率。

我们最常见的方式为进制哈希,进制哈希的话,我们可以设置一个固定的进制值,然后将这串数字上的每一个值都当作这个进制下的一个数字,这个串就可以看作是这个进制的哈希值,我们通过对比哈希值可以得到不同的操作

我们在实际操作中可以加上一个比较大的素数,这样可以降低出错概率,然后中间处理的时候进行mod(选择一个素数最好,绝大多数情况下,不要选择一个1e9
级别的数,根据生日悖论,这样出现相同的概率将会大概率上升)再比较是否相同,我们也可以选择两个1e9的质数来模,有一个1e18级别的也行,
但是我们有可能会被卡时间什么的,我们比较偷懒的方法就是不用mod,而且用
usigned long long ,让它自然溢出
因为一旦溢出的话,它会自动对2的64次进行取模,一般不会卡我们(自然溢出hash)

第一种的代码如下:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring> 
using namespace std;
typedef unsigned long long ull;
const int maxn=1e4+10;
const int prime=233317;
ull base=97,a[maxn],mod=21237044013013795711;
char s[maxn];
ull hhash(char s[])
{
	int len=strlen(s);
	ull ans=0;
	for(int i=0;i<len;i++)
	{
		ans=(ans*base+(ull)s[i])%mod+prime;
	}
	return ans;
}
int main()
{
	int n;
	while(cin>>n)
	{
		int ans=1;
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++)
		{
			scanf("%s",s);
			a[i]=hhash(s);
		}
		sort(a+1,a+n+1);
		for(int i=1;i<n;i++)
		{
			if(a[i]!=a[i+1])
			ans++;
		}
		printf("%d\n",ans);
	}
}

有时我们再hhash里面的最后一步我们也可以加上这样的一个操作
ans&0x7fffffff
意为对ans取正

当然了,上面的这种方法也有可能出错,为了降低错误我们可以运用另外的一个方法,就像我上面说过的一样,我么可以使用双重hash或者多重hash来做,得到的效果更好,然后可以选择用1e9级别的质数和1e18级别的质数(最上面的那个方法就是1e18的,不过有点难记)来操作

双重hash计算:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring> 
using namespace std;
typedef unsigned long long ull;
const int maxn=1e4+10;
ull base=131,moda=19260817,modb=19660813;
char s[maxn];
struct hhhash{
	ull x,y;
}a[maxn];
ull hhasha(char s[])
{
	int len=strlen(s);
	ull ans=0;
	for(int i=0;i<len;i++)
	{
		ans=(ans*base+(ull)s[i])%moda;
	}
	return ans;
}
ull hhashb(char s[])
{
	int len=strlen(s);
	ull ans=0;
	for(int i=0;i<len;i++)
	{
		ans=(ans*base+(ull)s[i])%modb;
	}
	return ans;
}
bool cmp(struct hhhash a,struct hhhash b)
{
	return a.x<b.x;
}
int main()
{
	int n;
	while(cin>>n)
	{
		int ans=1;
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++)
		{
			scanf("%s",s);
			a[i].x=hhasha(s);
			a[i].y=hhashb(s);
		}
		sort(a+1,a+n+1,cmp);
		for(int i=1;i<n;i++)
		{
			if(a[i].x!=a[i+1].x||a[i].y!=a[i+1].y)
			ans++;
		}
		printf("%d\n",ans);
	}
}

过一段时间再总结以下字典序的吧,自己不是很熟~~~呜呜呜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值