acwing-138 兔子与兔子(字符串哈希)

文章介绍了如何使用哈希值来优化字符串匹配问题,通过将字符串视为131进制数,计算每个子串的哈希值,并利用p数组记录进制幂次。在O(1)的时间复杂度内,可以求得字符串区间内的哈希值,从而判断两区间内字符串是否相等。这种方法在处理大量字符串比较时能显著提高效率。
摘要由CSDN通过智能技术生成

该算法的目的是将字符串转化为哈希值,这样的话在做字符串匹配的题的时候可以大大降低时间复杂度,仅需O(1)的时间复杂度即可。

首先要将字符串看成一个p进制的数,一般可以看作131或者13331。一般我们需要两个数组,p[N]用来存放这是131的第几次方,p[0]=1,h[N]用来存放字符串的哈希值。在存放哈希值时,需要将字符转化为数字。

当字符串都为小写字母时:

例如:字符串s为abcd,存放的分别是h[a],h[ab],h[abc],h[abcd]的哈希值。倘若我们知道h[abc]的哈希值,我们如何去求h[abcd]的哈希值呢?

求abcd的哈希值,也就相当于abc向高位进了一位,同时加上了字符d的值,d的值用1-26来表示为4。所以我们可以推出h[i]=h[i-1]*131+s[i](1-26的数),所以转换为h[i]=h[i-1]*131+s[i]-'a'+1。

    int n=strlen(s+1); //我们要使字符串的下标从1开始,所以输入的时候s+1
	for(int i=1;i<=n;i++)
	{
		h[i]=h[i-1]*base+s[i]-'a'+1; //将字符a-z转换为1-26,所以减一之后要加一 
		p[i]=p[i-1]*base;  //这里的base为131
	}

当我们要求一段区间内的哈希值时,又该如何去求呢?

我们需要求出[L,R]区间内的哈希值:h[L-R]=h[R]-h[L-1]*131^(R-L+1),其中L-1是从最低为到达现在的位置,一共要进(R-L+1)次方,举例说明一下:字符串abcdef,依次求出h[a],h[ab],h[abc],h[abcd],h[abcde],h[abcdef],那么b从一开始ab中的位置,到达abcdef中的b的位置,一共进了4个进制,也就是(R-L+1)进制,也就相当于b从一开始的个位到达了现在的万位,所以p数组要记录每一次的进制结果。

ULL get(int l,int r)
{
	return h[r]-h[l-1]*p[r-l+1]; //每次乘上131的(r-l+1)次方,(r-l+1)用p数组表示
}

 那么对于区间内的哈希值我们已经知道如何去求了,那么又该问求出哈希值了又有什么用呢?

看一道例题就明白了链接放在这里了:138. 兔子与兔子 - AcWing题库

大致题意就是要我们判断[l1,r1],[l2,r2]区间内的字符串是否相等,那么我们已经分别知道了这两个区间的哈希值,那么我们直接判断他们的哈希值是否相等不就好了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=10000000,base =131;
typedef unsigned long long ULL;
ULL h[N];
ULL p[N];
char s[N];
ULL get(int l,int r)
{
	return h[r]-h[l-1]*p[r-l+1]; //每次乘上131的(r-l+1)次方,(r-l+1)用p数组表示
}
int main()
{
	
	scanf("%s",s+1);
	p[0]=1; 
	int n=strlen(s+1);//提前算出来,不能写到for循环内,不然每次算一下就要超时
	for(int i=1;i<=n;i++)
	{
		h[i]=h[i-1]*base+s[i]-'a'+1; //将字符a-z转换为1-26,所以减一之后要加一 
		p[i]=p[i-1]*base;  
	}
	int m;
	scanf("%d",&m);
	while(m--)
	{
		int l1,r1,l2,r2;
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		if(get(l1,r1)==get(l2,r2))  printf("Yes\n");
		else  printf("No\n");
	}
	return 0;	
} 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值