给定一个长度为 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;
}