牛客的一道哈希字符串题(模板)
🎈M形字符串
题意
给一个长度为n的字符串(1<=n<=200000),他只包含小写字母
找到这个字符串多少个前缀是M形字符串.
M形字符串定义如下:
他由两个相同的回文串拼接而来,第一个回文串的结尾字符和第二个字符串的开始字符可以重叠,也就是以下都是M形字符串.
abccbaabccba(由abccba+abccba组成)
abcbaabcba(有abcba+abcba组成)
abccbabccba(由abccba+abccba组成组成,但是中间的1是共用的)
a(一个单独字符也算)
思路
hash字符串,从前遍历,然后从后遍历,这样可以直接o(n * 4)判断,折半判断是否是对称的,再折半判断是否对称。
gethash这步骤从前遍历是 has1[r]-has1[l-1] * mi[l-r+1];
gethash这步骤从后遍历是 has2[l]-has2[r+1] * mi[r-l+1];
AC代码
#include<bits/stdc++.h>//字符串哈希-回文子串
#define LL unsigned long long
using namespace std;
const int N=2e5+10;
LL mi[N],has1[N],has2[N];
int ans;
int n;
char s[N];
const int h=31;
LL gethas1(int x,int y){
return has1[y]-has1[x-1]*mi[y-x+1];
}
LL gethas2(int x,int y){
return has2[x]-has2[y+1]*mi[y-x+1];
}
int query1(int l,int r){
int mid=l+r>>1;
if(l+r==mid*2) return gethas1(l,mid-1)==gethas2(mid+1,r);
return gethas1(l,mid)==gethas2(mid+1,r);
}
int pan(int x){
if(x%2==0){
if(query1(1,x/2)&&query1(x/2+1,x)&&gethas1(1,x/2)==gethas2(x/2+1,x)){
return 1;
}
return 0;
}
else{
if(query1(1,x/2+1)&&query1(x/2+1,x)&&gethas1(1,x/2+1)==gethas2(x/2+1,x)){
return 1;
}
return 0;
}
}
int main(){
scanf("%s",s+1);
n=strlen(s+1),mi[0]=1;
for(int i=1;i<=n;i++){
has1[i]=has1[i-1]*h+(s[i]-'a');
mi[i]=mi[i-1]*h;
}
for(int i=n;i>=1;i--) has2[i]=has2[i+1]*h+(s[i]-'a');
ans=0;
for(int i=1;i<=n;i++){
if(pan(i))ans++;
}
printf("%d\n",ans);
return 0;
}
hash字符串模板
#define LL unsigned long long
const int h=31;
LL gethas1(int x,int y){//从前遍历
return has1[y]-has1[x-1]*mi[y-x+1];
}
LL gethas2(int x,int y){//从后遍历
return has2[x]-has2[y+1]*mi[y-x+1];
}
for(int i=1;i<=n;i++){
has1[i]=has1[i-1]*h+(s[i]-'a');
mi[i]=mi[i-1]*h;
}
for(int i=n;i>=1;i--) has2[i]=has2[i+1]*h+(s[i]-'a');