http://main.edu.pl/en/archive/oi/19/okr
给出一个字符串,多次询问其中一个子串 [L,R] 的最小循环节长度。
假设 ∑Ri=L(S[i]==′x′) 表示区间 [L,R] 里字母 ′x′ 的出现次数。
假设最小循环节长度为 t ,则 R−L+1t (最小循环节的出现次数)是 gcdt{∑Ri=L(S[i]==t)} 的约数。
因此我们可以枚举 gcdt{∑Ri=L(S[i]==t)} 的约数,得到枚举出的最小循环节的长度,这样枚举最小循环节的长度,枚举次数会很少。一个合法的循环节长度可以取 l ,当且仅当 [L,R−l] 部分和 [L+l,R] 相同,这个可以自行yy下为啥是正确的。。。详细证明不好证。。。这样的话,我们在枚举了最小循环节的长度后就可以用hash来 O(1) 判断所枚举的循环节长度是否是合法的了
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 510000
#define MOD 1000000009
using namespace std;
typedef long long int LL;
char s[MAXN];
LL hash[MAXN],pow[MAXN];
int n,q;
int sum[MAXN][26];
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b);
}
bool check(int L1,int R1,int L2,int R2)
{
LL hash1=((hash[R1]-pow[R1-L1+1]*hash[L1-1]%MOD)%MOD+MOD)%MOD;
LL hash2=((hash[R2]-pow[R2-L2+1]*hash[L2-1]%MOD)%MOD+MOD)%MOD;
return hash1==hash2;
}
int main()
{
scanf("%d",&n);
scanf("%s",s+1);
pow[0]=1;
for(int i=1;i<=n;i++)
pow[i]=pow[i-1]*30007%MOD;
for(int i=1;i<=n;i++)
hash[i]=(hash[i-1]*30007%MOD+s[i]-'a'+1)%MOD;
for(int i=1;i<=n;i++)
for(int alpha=0;alpha<26;alpha++)
{
sum[i][alpha]=sum[i-1][alpha];
sum[i][alpha]+=((s[i]-'a')==alpha);
}
scanf("%d",&q);
while(q--)
{
int L,R,ans=0;
scanf("%d%d",&L,&R);
int GCD=0,len=R-L+1;
for(int alpha=0;alpha<26;alpha++)
GCD=gcd(GCD,sum[R][alpha]-sum[L-1][alpha]);
for(int i=1;i*i<=GCD;i++)
{
if(check(L,R-len/i,L+len/i,R)) ans=max(ans,i);
if(check(L,R-len/(GCD/i),L+len/(GCD/i),R)) ans=max(ans,GCD/i);
}
printf("%d\n",len/ans);
}
return 0;
}