2795: [Poi2012]A Horrible Poem
Time Limit: 50 Sec Memory Limit: 128 MBSubmit: 518 Solved: 248
[Submit][Status][Discuss]
Description
给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节。
如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到。
Input
第一行一个正整数n (n<=500,000),表示S的长度。
第二行n个小写英文字母,表示字符串S。
第三行一个正整数q (q<=2,000,000),表示询问个数。
下面q行每行两个正整数a,b (1<=a<=b<=n),表示询问字符串S[a..b]的最短循环节长度。
Output
依次输出q行正整数,第i行的正整数对应第i个询问的答案。
Sample Input
8
aaabcabc
3
1 3
3 8
4 8
aaabcabc
3
1 3
3 8
4 8
Sample Output
1
3
5
3
5
考虑循环节长度一定是区间长度的约数,出现次数一定是每个字母出现次数的约数
所以先求循环节的可能出现次数,再判断是否合法
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
using namespace std;
#define MAXN 500010
#define MAXM 1010
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
#define ll long long
ll bas=233;
int n,m;
char s[MAXN];
ll h[MAXN];
ll mi[MAXN];
int S[MAXN][26];
int gcd(int x,int y)
{
return !y?x:gcd(y,x%y);
}
ll hs(int x,int y)
{
return (h[x+y-1]-h[x-1]*mi[y]%MOD+MOD)%MOD;
}
bool jud(int x,int y,int l)
{
return hs(x,y-x+1-l)==hs(x+l,y-x+1-l);
}
int main()
{
int i,j,x,y;
scanf("%d%s",&n,s+1);
mi[0]=1;
for(i=1;i<=n;i++)
{
mi[i]=mi[i-1]*bas%MOD;
h[i]=(h[i-1]*bas+s[i])%MOD;
for(j=0;j<26;j++)
{
S[i][j]=S[i-1][j]+((s[i]-'a')==j);
}
}
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&x,&y);
int c=y-x+1;
for(i=0;i<26;i++)
{
c=gcd(c,S[y][i]-S[x-1][i]);
}
if(c==1)
{
printf("%d\n",y-x+1);
continue ;
}
bool flag=0;
for(i=1;i*i<=c;i++)
{
if(!(c%i))
{
if(jud(x,y,(y-x+1)/c*i))
{
printf("%d\n",(y-x+1)/c*i);
flag=1;
break;
}
}
}
if(flag)
{
continue ;
}
for(i--;i;i--)
{
if(!(c%i))
{
if(jud(x,y,(y-x+1)/i))
{
printf("%d\n",(y-x+1)/i);
break;
}
}
}
}
return 0;
}