AcWing 841.字符串哈希

本文探讨了一种在字符串问题中快速判断两个子串是否相同的高效方法,结合了字符串哈希和线性探查的思想。通过将字符串转换为131进制的哈希值,解决了在线性时间内查找子串哈希值并进行比较的问题。适用于大规模数据的字符串相似性检查。
摘要由CSDN通过智能技术生成

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2请你判断 [l1,r1] 和 [l2,r2]这两个区间所包含的字符串子串是否完全相同。

字符串中只包含大小写英文字母和数字。

输入格式

第一行包含整数 n和 m,表示字符串长度和询问次数。

第二行包含一个长度为 n的字符串,字符串中只包含大小写英文字母和数字。

接下来 m行,每行包含四个整数 l1,r1表示一次询问所涉及的两个区间。

注意,字符串的位置从 1开始编号。

输出格式

对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1≤n,m≤10^5

输入样例:

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

输出样例:

Yes
No
Yes

       这里回忆一下传统的线性探查法的哈希方式:题目输入了a1,a2,a3,......ai-1,ai,ai+1,.....an,然后给我们一个数x,问我们这个数在不在刚才输入的数据里。这种题目我们就是先声明一个数组,然后将刚刚输入的一堆数据的通过某种手段映射成下标,然后在该下标的位置存储起来,这样查询一个数时,我们就可以根据数值算出它对应的下标,如果该下标处是空的,就说明没有,如果存在就是有。

        不过这都是理想情况,事实上在实现的时候,我们就是根据数据规模声明一个较大的数组,改数组长度是数据规模的2~3倍,例如数据规模是n,那么我们就声明一个大小为2n的数组,设N=2*n,那么映射下标的公式就是:int k = (x%N+N)%N,说白了就是取个模,不过当x小于N时,取模的结果是负数,下标不能是负的呀,所以我们再加上一个N,然后再取模一次。通过约定了这样一个存数的规则,我们就实现了数值和下标的转换,不过显而易见的是,我们这样做,肯定会发生冲突,也就是当两个数取模之后结果相同,该怎么办呢,解决方法就是如果算出来下标之后该下标的位置上有数字,就沿着该下标往后找,如果有空位置就填在该处,这就是线性探查法的思路,通过值算出下标,那么该在下标到往后不远的范围内,一定可以找到这个数(如果该数存在的话,如果不存在就会往后找的时候就会遇到空位置)。

字符串哈希和线性探查法完全是两种东西,字符串哈希就是给你一个很长的字符,然后问你其中两段子串是否相等,肯定不能用暴力,要不铁超时,先对这个长的字符串进行一个哈希:将一个长的字符串转换成一个P进制的数字,通过这种方式,我们能算出来它任意两个子串的哈希值,通过对比哈希值就能知道两个串是否相等。P进制我们就用131进制,然后数的大小就是2^64-1,这是固定套路,用131几乎不会产生冲突,就是两个不同的字符串却映射成同一个哈希值的现象不会发生。然后我们就可以写代码了,具体内容注释写的很细。

AC代码 

/*字符串哈希就是通过将某一个长的字符串转换成一个数字
*然后可以算出子串的哈希值,这样就可以知道该字符串的任意两个子串是否相等
* 固定套路就是将该字符串转成131进制的数字,然后用unsigned long long储存哈希值
*/

#include<iostream>
#include<string>
using namespace std;
typedef unsigned long long ULL;
const int N = 100010, P = 131;
ULL p[N], h[N];
int query(int l, int r) {
	/*我们已经知道0-l的哈希值和0-r的哈希值,据此我们可以计算出l-r的哈希值
	* 假如P为10,字符串“12345”,需要计算第3(l)位到第5(r)位的哈希值
	* 其实在10进制下一眼就能看出来是345,怎么算的呢,就是h[5]-h[2]*p[5-2],即12345-12*10^3=12345-12000=345;
	*/
	return h[r] - h[l - 1] * p[r - l + 1];
}
int main(void) {
	int n, m; cin >> n >> m;
	string str; cin >> str;
	//类似于初始化
	p[0] = 1;
	h[0] = 0;
	for (int i = 0; i < n; i++) {
		p[i + 1] = p[i] * P;//p[i]表示第i位字母占的权值,例如p[2]表示第2位是p的平方,类似于十进制下的10的平方
		h[i + 1] = h[i] * P + str[i];
		/*h[i]表示0-i字符串对应的哈希值,因为我们将一个字符串转换成了一个P进制的数字
		* 例如123这个数字,假如p等于10,那么h[1]表示‘1’对应的哈希值,为 str[1],即为1 ;
		* h[2]表示‘12’对应的哈希值,为h[1]*10+str[1],即1*10+2=12;
		* h[3]表示‘123’对应的哈希值,为h[2]*10+str[2],即10*10+3=123
		* 通过h[i+1]=h[i]*p+str[i]的公式就计算出了0-i的所有哈希值,只不过在本题里面将str[i],也就是ascll码值当做值
		*/
	}
	while (m--) {
		int l1, r1, l2, r2;
		cin >> l1 >> r1 >> l2 >> r2;
		if (query(l1, r1) == query(l2, r2))cout << "Yes" << endl;
		else cout << "No" << endl;
	}
	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值