题目大意:给定一个字符串,多次询问某一子串的循环节
首先循环次数一定是子串长度的约数
因此我们可以枚举子串长度的约数进行验证
验证时选择Hash,验证[x,y-len]和[x+len,y]这两段是否相等,O(1)即可出解
但是这样做总复杂度是O(q√n)的,会T
考虑一个优化,设某个字母在子串中出现了k次,那么循环次数一定是k的约数
因此我们取每个字母在子串中出现次数的Gcd,枚举这个值的约数即可
时间复杂度是O(26q+q√n) 真是不优雅- -
#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;
}
BZOJ 2795 Poi2012 A Horrible Poem Hash
原文地址:http://blog.csdn.net/popoqqq/article/details/42916081