[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;
}
posted @ 2025-06-18 09:44  fzrcy  阅读(12)  评论(0)    收藏  举报