洛谷P3538 [POI2012] OKR-A Horrible Poem
前言
比较典,可以当模板题,故记录一下,写的可能比较水。
题意
长度为 \(n\ (\leq 6\times 10^5)\) 的字符串,有 \(q\ (\leq 2\times 10^6)\) 个询问,每次询问求一个区间的最小循环节。
思路
题面看起来很唬人,我们平时求最短循环节都是用前缀函数,这一放在区间上就不会做了。
但实际上很简单,最小循环节的长度一定是串长的因数,因此直接枚举即可,用线性筛记录每个数最大的质因数来优化,不断除以自己最大的质因数即可枚举所有因数。
至于判断一个长度是否为循环节,可以用哈希,设长度为 \(len\),如果 \(\operatorname{Substr}[l,r-len]=\operatorname{Substr}[l+len,r]\) 则说明该长度为循环节长度。
复杂度当然就是 \(O(q\cdot d(n))\),这个 \(d(n)\) 就很玄学,据说可以证明最多 \(O(\sqrt n)\),但实际经常跑不满,反正卡常题。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN=5e5,MAXQ=2e6;
LL bs=13131,bspw[MAXN+5],P=1e9+321;
int n,q;
char str[MAXN+5];
inline void init_bspw(){
bspw[0]=1;
for(int i=1;i<=n;i++)
bspw[i]=bspw[i-1]*bs%P;
}
struct HashNode{
LL val;
int len;
HashNode(){}
HashNode(LL a,int b):val(a),len(b){}
};
inline HashNode operator + (const HashNode& a,const HashNode& b){
HashNode res;
res.val=(a.val*bspw[b.len]%P+b.val)%P;
res.len=a.len+b.len;
return res;
}
inline HashNode operator - (const HashNode& a,const HashNode& b){//b is prefix of a
HashNode res;
res.val=(a.val-b.val*bspw[a.len-b.len]%P+P)%P;
res.len=a.len-b.len;
return res;
}
inline bool operator == (const HashNode& a,const HashNode& b){
return (a.len==b.len) && (a.val==b.val);
}
class HashString{
private:
HashNode hs[MAXN+5];
public:
inline void build(){
hs[0]=HashNode(0,0);
for(int i=1;i<=n;i++)
hs[i]=HashNode(str[i]-'a'+1,1);
for(int i=1;i<=n;i++)
hs[i]=hs[i-1]+hs[i];
}
inline HashNode query(const int& l,const int& r) const{
return hs[r]-hs[l-1];
}
}hs;
vector<int>prime;
bool vis[MAXN+5];
int divisor[MAXN+5];
inline void init_prime(){
for(int i=2;i<=n;i++){
if(!vis[i]) prime.push_back(i),divisor[i]=i;
for(auto it:prime){
if(1LL*it*i>n) break;
vis[it*i]=true;
divisor[it*i]=it;
if(i%it==0) break;
}
}
}
inline bool check(int l,int r,int len){
return hs.query(l,r-len)==hs.query(l+len,r);
}
int main(){
scanf("%d",&n);
scanf("%s",str+1);
init_bspw();
init_prime();
hs.build();
scanf("%d",&q);
int l,r,len,tmp;
while(q--){
scanf("%d %d",&l,&r);
len=tmp=r-l+1;
while(tmp>1){
if(check(l,r,len/divisor[tmp])) len/=divisor[tmp];
tmp/=divisor[tmp];
}
printf("%d\n",len);
}
return 0;
}