给定一个长度为 n
的字符串,再给定 m
个询问,每个询问包含四个整数 l1,r1,l2,r2
,请你判断 [l1,r1]
和 [l2,r2]
这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n
和 m
,表示字符串长度和询问次数。
第二行包含一个长度为 n
的字符串,字符串中只包含大小写英文字母和数字。
接下来 m
行,每行包含四个整数 l1,r1,l2,r2
,表示一次询问所涉及的两个区间。
注意,字符串的位置从 1
开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤n,m≤105
此题只是其中一例,本萌新认为此算法比较重要,核心思想是把字符串转化成一个值,然后就可以分别与其他的字符串比较来看两串是否相同,我们知道十进制十进一,二进制是二进一,在此我踩在前人肩膀上,取此值为131,即131进一(前缀和的思想来模拟进位),而我们还需要考虑值的大小问题,在此又可借鉴前人之智慧,取2的64次方,而unsigned long long 刚好是所需值,为方便理解前缀和,字符串的录入从下标为1的开始。
此题是求一个字符串中指定字串是否相等,我们用p数组来模拟的进位,即类似于十进制的个十百千万位,用d[ i ]数组来表示从下标1到i的s字符串的大小
第二个图是表示d数组,即映射值,d1表示s[1]的哈希值,d2表示s[2]的哈希值, 我们要求l到r的哈希值,而我们已知d[ l -1]和d[ r ],此时我们可以利用d[ r ]-d[l-1]*p[r-l+1]来表示其哈希值。例如字符串ccbbddb,l=3,r=6,整个字符串转化为p进制为(ccbbddb)p,而s[ l-1 ]为(cc)p,s[r]为(ccbbdd)p,当s[ l -1]*p[r-l+1]为(cc0000),相减后就为(bbddb)p,此时发现就是所求的哈希值。
加入代码解释,如若不理解,萌新入住,胆小勿喷。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100010,P=131;//P为p进制
char s[N];//此来存储字符串
unsigned long long p[N],d[N];//开2的64次方来进行存储数据,p数组是代表进位,即p,p*p,p*p*p……,类似十进制
int Haxistring(int l,int r)
{
return d[r]-d[l-1]*p[r-l+1];//此不在赘述
}
int main()
{
int n,m;//n为字符串长度,m为多少次询问
scanf("%d%d%s",&n,&m,s+1);//s+1是将s[1]作为开端,众所周知,指针后移1
p[0]=1;//给其初值,为前缀和形式求进位做准备
for(int i=1;i<=n;i++)//从一开始,利于理解,更方便
{
p[i]=p[i-1]*P;//求进位
d[i]=d[i-1]*P+s[i];//利于前缀和求s[1]到s[1],s[2],s[……],s[n]之间的哈希值
}
while(m--)
{
int l1,l2,r1,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(Haxistring(l1,r1)==Haxistring(l2,r2)) puts("Yes");
else puts("No");
}