正题
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P3538
题目大意
给一个字符串,有q个询问,询问一个区间最短循环节。
解题思路
首先最短循环节长度一定长度的约数,所以我们可以枚举约数,然后判断循环节的话只需要
和
这段区间相等就好了,所以我们可以用hash表来
来判断循环节。
时间复杂度:
然后在约数的地方我们用素数筛来做,将
的复杂度降低为了
最终时间复杂度:
code
#include<cstdio>
#define ull unsigned long long
using namespace std;
const int wer=13131,N=500005;
ull mul[N],hash[N];
bool use[N];
int next[N],prim[N/10],ys[N/10],n,m,l,r,k,tot;
char s[N];
void prime()//素数筛优化约数
{
for(int i=2;i<=n;i++)
{
if(!use[i]){
prim[++k]=i;
next[i]=i;
}
for(int j=1;j<=k&&(long long)prim[j]*i<=n;j++)
{
next[prim[j]*i]=prim[j];
use[prim[j]*i]=true;
if(i%prim[j]==0){
break;
}
}
}
}
bool check(int l1,int r1,int l2,int r2)
{
return hash[r1]-hash[l1-1]*mul[r1-l1+1]==hash[r2]-hash[l2-1]*mul[r2-l2+1];
}
int main()
{
scanf("%d\n%s",&n,&s);
mul[0]=1;
for(int i=1;i<=n;i++){
mul[i]=mul[i-1]*wer;
hash[i]=hash[i-1]*wer+s[i-1]-'a'+1;
}//哈希
prime();
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&l,&r);
int len=r-l+1;
tot=0;
while(len!=1){
ys[++tot]=next[len];//记录素数
len/=next[len];
}
len=r-l+1;
for(int j=1;j<=tot;j++)//枚举约数
{
int t=len/ys[j];
if(check(l,r-t,l+t,r)){
len=t;
}//判断
}
printf("%d\n",len);
}
}