字符串哈希

字符串哈希

字符串哈希可以将一个字符串转化为一个数字,可以做到加速比较的作用。一般用于比较子串

【模板】字符串哈希

这道题最简单的方式是set

友情提醒:如果真的想好好练习哈希的话,请自觉。

友情提醒:如果真的想好好练习哈希的话,请自觉。

友情提醒:如果真的想好好练习哈希的话,请自觉。

所以我们用哈希
于是我们可以在输入之后算出哈希值,查询此哈希值是否出现过。如果没有就给答案加一。
我写的是自然溢出、双哈希。
自然溢出就是使用unsigned long longunsigned int,就拿unsigned long long举例,使用unsigned long long如果结果超出unsigned long long的范围那么结果就会对 2 64 2^{64} 264取模。
双哈希就是每个字符串有两个哈希值,这样哈希碰撞的概率就会减小。
哈希碰撞是两个不一样的字符串的哈希值一样,导致答案错误。
求哈希值代码:

unsigned long long get_hash(string s,int x)//x代表第多少个进制数
{
	unsigned long long sum=0;//自然溢出
	for(int i=0;i<s.size();i++)//枚举字符串的每一位
	{
		sum=(sum*b[x]+s[i]);//b[x]进制
	}
	return sum%mod;//模mod不然会爆数组
}

完整代码(无注释)方便粘贴

#include<bits/stdc++.h>
using namespace std;
const int mod=998245,b[3]={0,13331,131};
int n,cnt;
bool f[10000005][3];
string s;
unsigned long long get_hash(string s,int x)
{
	unsigned long long sum=0;
	for(int i=0;i<s.size();i++)
	{
		sum=(sum*b[x]+s[i]);
	}
	return sum%mod;
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		if(f[get_hash(s,1)][1]&&f[get_hash(s,2)][2])continue;
		f[get_hash(s,1)][1]=1;
		f[get_hash(s,2)][2]=1;
		cnt++;
	}
	cout<<cnt;
	return 0;
}

P10468 兔子与兔子

这道题其实就是让我们每次取两个子串来进行比较。
所以这次我们需要求出子串的哈希值。
我们可以使用前缀和的思想。
所以我们可以得到一个公式hs[r][x]-hs[l-1][x]*pw[r-l+1][x]
hs[i][x]表示在进制数b[x]下1~i的哈希值,pw[i][x]是b[x]的i次方。
所以代码又写完了。
完整代码

#include<bits/stdc++.h>
using namespace std;
const int b[3]={0,911,131};
int n,m,l1,r1,l2,r2;
unsigned long long hs[1000005][3],pw[1000005][3];
string s;
void init_hash(string s,int x)
{
	pw[0][x]=1;
	for(int i=1;i<=n;i++)
	{
		pw[i][x]=pw[i-1][x]*b[x];
		hs[i][x]=hs[i-1][x]*b[x]+s[i];
	}
}
unsigned long long get_hash(int l,int r,int x)
{
	return hs[r][x]-hs[l-1][x]*pw[r-l+1][x];
}
int main()
{
	cin>>s;
	n=s.size();
	s=" "+s;
	cin>>m;
	init_hash(s,1);
	init_hash(s,2);
	for(int i=1;i<=m;i++)
	{
		cin>>l1>>r1>>l2>>r2;
		if(get_hash(l1,r1,1)==get_hash(l2,r2,1)&&get_hash(l1,r1,2)==get_hash(l2,r2,2))
		{
			cout<<"Yes\n";
		}
		else
		{
			cout<<"No\n";
		}
	}
	return 0;
 } 

这题和上一道题的区别就是要预处理出哈希值数组和b[x]的幂次,以便O(1)求出区间哈希值。

CF39J Spelling Check CF

CF39J Spelling Check VJ
题目简化:有几种删除方式能使字符串变成目标字符串,并输出删除的位置。
于是我们可以推一个删除一个字符之后的哈希值的式子。
return hs[pos-1][x]*pw[l-pos][x]+get_hash(pos+1,l,x);
get_hash是一段的哈希值。
完整代码(命名有点乱)

#include<bits/stdc++.h>
using namespace std;
const int mod=999833,b[5]={0,911,131,13331,9191};
unsigned long long lt1,lt2,l,cnt;
unsigned long long hs[1000006][3],pw[1000006][3];
string s,t;
void hack(string s,int x)
{
	pw[0][x]=1;
	for(int i=1;i<=l;i++)
	{
		hs[i][x]=(hs[i-1][x]*b[x]+s[i]);
		pw[i][x]=pw[i-1][x]*b[x];
	}
}
unsigned long long make(string s,int x)
{
	unsigned long long sum=0;
	for(int i=1;i<s.size();i++)
	{
		sum=(sum*b[x]+s[i]);
	}
	return sum;
}
unsigned long long get_hash(int l,int r,int x)
{
	return hs[r][x]-hs[l-1][x]*pw[r-l+1][x];
}
unsigned long long dis(int pos,int x)
{
	return hs[pos-1][x]*pw[l-pos][x]+get_hash(pos+1,l,x);
}
signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>s>>t;
	l=s.size();
	s=" "+s;
	hack(s,1);
	hack(s,2);
	t=" "+t;
	lt1=make(t,1);
	lt2=make(t,2);
	for(int i=1;i<=l;i++)
	{
		if(dis(i,1)==lt1&&dis(i,2)==lt2)
		{
			cnt++;
		}
	}
	cout<<cnt<<'\n';
	for(int i=1;i<=l;i++)
	{
		if(dis(i,1)==lt1&&dis(i,2)==lt2)
		{
			cout<<i<<' ';
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值