牛客的一道哈希字符串题(模板)

🎈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');

posted @ 2021-04-25 17:51  ouluy  阅读(69)  评论(0编辑  收藏  举报