题解 [POI2012] OKR-A Horrible Poem

发布时间 2023-08-07 10:55:01作者: 2017BeiJiang

题目链接

询问循环节的“模板题”?

首先,有一个经典结论:若存在一长度为 \(len\) 的循环节,则 \(s[l \sim r-len]=s[l+len \sim r]\),简单来说就是利用移位,说明是否是循环节。

有了这个结论,再使用字符串哈希,我们就可以做到 \(O(1)\) 判断。

因为:字符串长度=循环节长度 \(\times\) 循环次数。
所以我们要么枚举循环节长度,要么枚举循环次数。两个都得是字符串长度的因数。

如果枚举循环节长度,考虑先预处理出所有长度的因数,时间为 \(O(n\log n)\),每次查询最坏的可能是 \(O(q \sqrt n )\),最坏情况下无法通过。

而循环节长度显然是不好优化的,因为如果长度为 \(1\) 的不行,不能说明长度为 \(2\) 的不行。而我们要求最短循环节,所以只能从小到大枚举,不利于优化。

考虑枚举循环次数(转化为求循环次数最大),若循环次数为 \(3\) 的不行,那么循环次数为 \(6,9,12...\) 的肯定也不行。

于是我们将长度质因数分解,对于每个质因数,如果除完之后可以,那么我们就除,用筛法记录下每个数字的最小质因子方便快速分解。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ULL;
typedef long long LL;

LL read() {
    LL sum=0,flag=1; char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') flag=-1; c=getchar();}
    while(c>='0'&&c<='9') {sum=sum*10+c-'0'; c=getchar();}
    return sum*flag;
}

const int N=5e5+10,M=5e5;
const LL MOD1=1e9+7,MOD2=998244353,base=13331;
int n,q;
LL p1[N],p2[N],h1[N],h2[N];
string s;
int f[N],fac[N];

void init() {
    for(int i=2;i<=M;i++) {
        if(f[i]) continue;
        for(int j=1;j*i<=M;j++) {
            if(!fac[i*j]) fac[i*j]=i;
            f[i*j]=1;
        }
    }
}

LL get1(int l,int r) {
    return ((h1[r]-h1[l-1]*p1[r-l+1])%MOD1+MOD1)%MOD1;
}

LL get2(int l,int r) {
    return ((h2[r]-h2[l-1]*p2[r-l+1])%MOD2+MOD2)%MOD2;
}

int main() {
    // freopen("a.in","r",stdin);
    // freopen("a.out","w",stdout);

    init();
    cin>>n>>s;
    s=" "+s;
    p1[0]=p2[0]=1;
    for(int i=1;i<=n;i++) {
        p1[i]=p1[i-1]*base%MOD1;
        p2[i]=p2[i-1]*base%MOD2;
        h1[i]=(h1[i-1]*base+s[i]-'a')%MOD1;
        h2[i]=(h2[i-1]*base+s[i]-'a')%MOD2;
    }
    cin>>q;
    while(q--) {
        int l,r; l=read(); r=read();
        int len=r-l+1,ans=r-l+1;
        while(len>1) {
            if(get1(l,r-ans/fac[len])==get1(l+ans/fac[len],r)) {
                ans/=fac[len];
            }
            len/=fac[len];
        }
        cout<<ans<<'\n';
    }
    
    return 0;
}
*/