哈希与字符串(超级明了的哈希算法解决字符串问题) 兔子与兔子(Acwing 138)

本文介绍了一种利用哈希算法优化字符串匹配的方法,通过将字符串转换为基于特定基数的数字,实现快速查找子串。文章详细解释了算法原理,包括如何计算哈希值、处理溢出和高效更新哈希值等关键步骤,并提供了具体实例和AC代码。

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

哈希算法

哈希算法的思维方式就是把一个字符串转化为一个以Base进制的数字。

我们通常会遇到这种问题 给你一个字符串用A表示,然后又再给你一个字符串B判断B这个字符串是否在A中或者判断B在字符串A中出现的次数,如果我们用暴力方法,很大可能会超时 因为有很多都要进行回溯操作所以不能暴力解题。

在这里我们就可以考虑用哈希算法,根据这个算法的思维,你是不是就可以把B转化为数字,然后只用判断A中有没有这个数字就可以了。

先举一个例子吧

比如 abc  这个字符串要转化为数字怎么转化呢??
比如要转化为 131 进制的
我们先写下
a b c对应的十进制是1  2  3
那么转化为131进制就是
a 对应的是 1*131的0次方
ab对应的是1*131的1次方+2
abc对应的是1*131的2次方+2*131的1次方+3*131的0次方
然后我们再思考一下 如果在abcd呢  那abcd的字符串  现在我们知道了abc的字符串我们如何根据这个abc的字符串求出abcd的字符串呢?
比如h[3]存的是abc的字符串对应的数字那么h[4]=h[3]*131+4  h[4]就是对应abcd字符串的数字了
那么怎么推导出来这个公式的呢? 我们把h[3]都乘以131是不是位次上升1位 我们再加上末尾的数字d对应的数值是4
那么问题来了  我们知道abcd字符串对应的值 如何求中间的值 也就是 bcd的值呢?
我们可以拿着abcd的数值减去a的数值*131的三次方也就是h[4]-h[1]*131的三次方就得到bcd的数值了
可是131的三次方的数值怎么保存呢 我们可以另外开一个数组p来保存131的n次方的数值

可是如果131的次方太大了就会溢出 ,所以我们要拿这个数字mod2的64次方为啥是2的64次方因为unsigned long long最大就是2的64次方  如果用这个的话 我们不用对2的64次方取余数溢出了多少就打印多少 所以用2的64次方

基本的模版如下

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;//为什么要用unsigned long long呢 因为用unsigned long long是2的64次方我们就不用mod这个数字了 
const int N= 1000010,base=131;
char str[N];
ull h[N],p[N];//h数组是求【1,n】字符串的数字  p数组是求base的n次方 
ull get(int l,int r)
{
	return h[r]-h[l-1]*p[r-l+1];
 } 
 int main()
 {
 	scanf("%s",str+1);
 	int n=strlen(str+1);
 	p[0]=1;
 	for(int i=1;i<=n;i++)
 	{
 		h[i]=h[i-1]*base+str[i]-'a'+1;
		p[i]=p[i-1]*base; 
	 }
	 int m;
	 cin>>m;
	 while(m--)
	 {
	 	int l,r;
	 	cin>>l>>r;
	 	cout<<get(l,r)<<endl;
	 }
	 return 0;
 }

 

兔子与兔子

很久很久以前,森林里住着一群兔子。

有一天,兔子们想要研究自己的 DNA 序列。

我们首先选取一个好长好长的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 个小写英文字母)。

然后我们每次选择两个区间,询问如果用两个区间里的 DNA 序列分别生产出来两只兔子,这两个兔子是否一模一样。

注意两个兔子一模一样只可能是他们的 DNA 序列一模一样。

输入格式

第一行输入一个 DNA 字符串 S。

第二行一个数字 m,表示 m 次询问。

接下来 m 行,每行四个数字 l 1 ,r 1 ,l 2 ,r 2  l1,r1,l2,r2 ,分别表示此次询问的两个区间,注意字符串的位置从1开始编号。

输出格式

对于每次询问,输出一行表示结果。

如果两只兔子完全相同输出 Yes,否则输出 No(注意大小写)。

数据范围

1≤length(S),m≤1000000 1≤length(S),m≤1000000

输入样例:

aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2

输出样例:

Yes
No
Yes

 

AC代码如下

 

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ull;//为什么要用unsigned long long呢 因为用unsigned long long是2的64次方我们就不用mod这个数字了 
const int N= 1000010,base=131;
char str[N];
ull h[N],p[N];//h数组是求【1,n】字符串的数字  p数组是求base的n次方 
ull get(int l,int r)
{
	return h[r]-h[l-1]*p[r-l+1];
 } 
 int main()
 {
 	scanf("%s",str+1);
 	int n=strlen(str+1);
 	p[0]=1;
 	for(int i=1;i<=n;i++)
 	{
 		h[i]=h[i-1]*base+str[i]-'a'+1;
		p[i]=p[i-1]*base; 
	 }
	 int m;
	 cin>>m;
	 while(m--)
	 {
	 	int l,r,l1,r1;
	 	cin>>l>>r>>l1>>r1;
	 	if(get(l,r)==get(l1,r1))
	 	{
	 		cout<<"Yes"<<endl; 
		 }
		 else
		 {
		 	cout<<"No"<<endl;
		 }
	 }
	 return 0;
 }

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值