题目
求子串的最短循环节长度
分析
首先,有一些很重要的要说
- 循环节长度|子串长度
- 若 是某段的循环节,那么 也是循环节
- 若 是某段的循环节,满足$[l…r-x]=[l+x…r]
所以可以通过这样的方法,首先线性筛质数,从大到小枚举约数,求得答案
代码
#include <cstdio>
unsigned int n,m,prime[41541],q,v[500001],p[261],pw[500001],key[500001]; char s[500001];
int in(){
int ans=0; char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
bool check(int l1,int r1,int l2,int r2){
return key[r1]-pw[r1-l1+1]*key[l1-1]==key[r2]-pw[r2-l2+1]*key[l2-1];
}
int main(){
n=in(); scanf("%s",s+1); q=in(); pw[0]=1;
for (register int i=2;i<=n;i++){//线性筛
if (!v[i]) v[i]=i,prime[++m]=i;
for (register int j=1;j<=m;j++){
if (prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
for (register int i=1;i<=n;i++) pw[i]=pw[i-1]*263,key[i]=key[i-1]*263+s[i]-96;//哈希值
while (q--){
int l=in(); int r=in();
int len=r-l+1,k=0;
while (len>1){//可能的枚举方案
p[++k]=v[len];
len/=v[len];
}
len=r-l+1;
for (register int i=1;i<=k;i++){
int t=len/p[i];
if (check(l,r-t,l+t,r)) len=t;//O(1)查询
}
printf("%d\n",len);
}
return 0;
}