BZOJ 2795 Poi2012 A Horrible Poem Hash

题目大意:给定一个字符串,多次询问某一子串的循环节

首先循环次数一定是子串长度的约数

因此我们可以枚举子串长度的约数进行验证

验证时选择Hash,验证[x,y-len]和[x+len,y]这两段是否相等,O(1)即可出解

但是这样做总复杂度是O(q√n)的,会T

考虑一个优化,设某个字母在子串中出现了k次,那么循环次数一定是k的约数

因此我们取每个字母在子串中出现次数的Gcd,枚举这个值的约数即可

时间复杂度是O(26q+q√n) 真是不优雅- -

此外说分解质因数是O(nlogn)那位什么心态0- -

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 500500
#define BASE 157
using namespace std;
typedef unsigned long long ll;
int n,m,ans,cnt[26][M];
char s[M];
ll sum[M],power[M];
void Check(int x,int y,int times)
{
	int len=(y-x+1)-(y-x+1)/times;
	ll hash1=sum[x+len-1]-sum[x-1]*power[len];
	ll hash2=sum[y]-sum[y-len]*power[len];
	if(hash1==hash2)
		ans=max(ans,times);
}
void Query(int x,int y)
{
	int i,gcd=(y-x+1);
	ans=0;
	for(i=0;i<26;i++)
		gcd=__gcd(gcd,cnt[i][y]-cnt[i][x-1]);
	for(i=1;i*i<=gcd;i++)
		if(gcd%i==0)
			Check(x,y,i),Check(x,y,gcd/i);
}
int main()
{
	int i,j,x,y;
	cin>>n;
	scanf("%s",s+1);
	for(i=1;i<=n;i++)
		sum[i]=sum[i-1]*BASE+s[i];
	for(power[0]=1,i=1;i<=n;i++)
		power[i]=power[i-1]*BASE;
	for(j=0;j<26;j++)
		for(i=1;i<=n;i++)
			cnt[j][i]=cnt[j][i-1]+(s[i]-'a'==j);
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		Query(x,y);
		printf("%d\n",(y-x+1)/ans);
	}
	return 0;
}


你可能感兴趣的:(hash,bzoj,BZOJ2795)