#哈希,数论#洛谷 3538 OKR-A Horrible Poem

题目

求子串的最短循环节长度


分析

首先,有一些很重要的要说

  1. 循环节长度|子串长度
  2. n 是某段的循环节,那么 k n 也是循环节
  3. n 是某段的循环节,满足$[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;
}

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/81748069