字符串哈希
给定一个长度为 n 的字符串,再给定 m个询问,每个询问包含四个整数 l1,r1,l2,r2请你判断 [l1,r1][l1,r1] 和 [l2,r2][l2,r2] 这两个区间所包含的字符串子串是否完全相同。
字符串中只包含大小写英文字母和数字。
输入格式
第一行包含整数 n 和 m,表示字符串长度和询问次数。
第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。
接下来 m 行,每行包含四个整数 l1,r1,l2,r2表示一次询问所涉及的两个区间。
注意,字符串的位置从 1 开始编号。
输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes
,否则输出 No
。
每个结果占一行。
数据范围
1≤n,m≤105
输入样例:
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes
#include
using namespace std ;
typedef unsigned long long ULL ;
const int N = 1e5+10 , P = 131; //P一般采用131,或者是13331。根据经验判断,这样的将字符串映射成为数值的时候,99%的情况下是不h
ULL h[N] , p[N] ;
//h数组其实就是可以看作是存储前缀和的数组
//p其实就是记录当前的进制的数组,当前的位数的价值可以这么理解
ULL ToPnumber(int l , int r){
return h[r]-h[l-1]*p[r-l+1] ;
}
/*这个思想就比较妙了,想要算出有固定起始区间的一段字符串的P进制数,其实可以拿少的数扩展来进行相减。
举个例子,比如一段数组453672,我想要计算出来3~5区间的数值即367.我可以用45367-45*10的(5-3+1)次方。
同样的道理,想要算出l~r的区间代表的P进制的数字,目前有已知h[r]和h[l].直接return h[r]-h[l-1]*(r-l+1) ;
*/
int main(){
int n , m ;
cin>>n>>m ;
string x ;
cin>>x ;
h[0] = 0 ; p[0] = 1 ;
for(int i = 0 ;i < n ;++i)
{
p[i+1] = p[i]*P ;
h[i+1] = h[i]*P + x[i] ; //这就是算前缀和的操作
}
for(int i = 0 ; i < m ;++i)
{
int l1 , l2 , l3 ,l4 ;
cin>>l1>>l2>>l3>>l4 ;
if(ToPnumber(l1,l2) == ToPnumber(l3,l4))
cout<<"Yes"<< endl ;
else
cout<<"No"<< endl ;
}
return 0 ;
}