P3538 [POI2012]OKR-A Horrible Poem
P3538 [POI2012]OKR-A Horrible Poem
Describe:
第一行一个正整数 \(n(n \le 500 000)\),表示 \(S\) 的长度。
第二行n个小写英文字母,表示字符串 \(S\)。
第三行一个正整数 \(q(q \le 2 000 000)\),表示询问次数。
下面q行每行两个正整数\(a,b(1\le a\le b\le n)\),表示询问字符串 \(S[a…b]\) 的最短循环节长度。
Sol:
显然的,最短循环长度的 \(k\ (k\in Z)\) 倍一定是 \(b-a+1\),也就是说循环长度 \(len\) 一定是长度的因子。
再就因为是循环的,所以从 \(l \to l+len-1 \ \text{和} r-len+1 \to r\) 应该是一样的。
那么我们就可以用哈希做到 \(\mathcal{O(1)}\)
注意的是,枚举因子要从大到小枚举,另外,我们可以利用线性筛是利用枚举最小质因子的方式来记录下每个数的最小质因子,然后除一下找到最大因子,以此类推。
Code:
/*
Knowledge : Rubbish Algorithm
Work by :Gym_nastics
Time : O(AC)
*/
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int Mod=1e9+7;
const int Base=131;
const int N=1e6+6;
int read() {
int x=0,f=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f|=(ch=='-');
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
return f?-x:x;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+48);
}
int Prime[N],M[N],cnt,h[N],P[N];bool vis[N];
void prepare(){
for(int i=2;i<=1e6;i++){
if(!vis[i]) Prime[++cnt]=i,M[i]=i;
for(int j=1;j<=cnt&&i*Prime[j]<=1e6;j++){
vis[i*Prime[j]]=1;M[i*Prime[j]]=Prime[j];
if(!(i%Prime[j])) break;
}
}
}
char ch[N];
signed main() {
int l=read();cin>>ch+1;int Q=read();P[0]=1;prepare();
for(int i=1;i<=l;i++) h[i]=h[i-1]*Base+ch[i],P[i]=P[i-1]*Base;
while(Q--){
int l=read(),r=read(),len,ans;
ans=len=r-l+1;while(len^1){
int k=ans/M[len];len/=M[len];
if(!(h[r-k]-h[l-1]*P[r-k-l+1]^h[r]-h[l-1+k]*P[r-k-l+1])) ans=k;
}print(ans);putchar('\n');
}
return 0;
}