[POI 2012] OKR-A Horrible Poem
题目简述:给一个由小写字母构成的字符串 \(S\),长度为 \(n\le 5\times 10^5\)。有 \(q\le 10^6\) 个询问,每次询问 \(S\) 的子串 \(S_{l,r}\) 的最短循环节。
Analysis:
设 \(S_{l,r}\) 的长度为 \(len\),显然最短循环节长度 \(x\) 为 \(len\) 的因数。
对于判断 \(x\) 是否为循环节我们可以使用哈希函数判断 \(S_{l,r-d}=S_{l+d,r}\),可以 \(O(1 )\) 判断。
对于 \(S_{l,r}\) 的任意一个循环节,其长度一定是 \(x\) 的倍数。
设目前已知 \(t\) 为 \(S_{l,r}\) 的一个循环节,由唯一分解定理可知,\(x\) 可以用 \(t\) 除以若干个质数得到,且在做除法的过程中得到的数字都为 \(S_{l,r}\) 的一个循环节。因此我们可以不断地找 \(t\) 的一个质因子 \(p\),判断 \(t/p\) 是否合法,若合法则使 \(t=t/p\),不合法则找其它的质因子判断,由于一个数的质因子不超过 \(\log n\) 个,所以复杂度为 \(O(\log n)\) 。
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N=5e5+9;
const ull base=107;
ull H[N],p[N]; char s[N]; int n,q,mpr[N],pr[N],pc;
inline void Hash(){
p[0]=1;
for(int i=1;i<=n;i++)
H[i]=H[i-1]*base+s[i]-'a'+1,
p[i]=p[i-1]*base;
}
inline void sieve(){
for(int i=2;i<=n;i++){
if(!mpr[i])mpr[i]=pr[++pc]=i;
for(int j=1;j<=pc&&i*pr[j]<=n;j++){
mpr[i*pr[j]]=pr[j];
if(mpr[i]==pr[j])break;
}
}
// cout<<mpr[8]<<' '<<mpr[5]<<endl;
}
inline ull ask(int x,int y){return --x,H[y]-H[x]*p[y-x];}
inline bool judge(int l,int r,int d){return ask(l,r-d)==ask(l+d,r);}
int main(){
scanf("%d %s",&n,s+1),Hash(),sieve(),scanf("%d",&q);
for(int l,r,len,tmp;q--;){
scanf("%d%d",&l,&r),tmp=len=r-l+1;
for(;tmp!=1;tmp/=mpr[tmp])
if(judge(l,r,len/mpr[tmp]))len/=mpr[tmp];
printf("%d\n",len);
}
return 0;
}

浙公网安备 33010602011771号