正题
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P3538
题目大意
给一个字符串,有q个询问,询问一个区间最短循环节。
解题思路
首先最短循环节长度一定长度的约数,所以我们可以枚举约数,然后判断循环节的话只需要(l,r−t)(l,r−t)和(l+t,r)(l+t,r)这段区间相等就好了,所以我们可以用hash表来O(1)O(1)来判断循环节。
时间复杂度:O(qn−−√)O(qn)
然后在约数的地方我们用素数筛来做,将n−−√n的复杂度降低为了log nlogn
最终时间复杂度:O(q log n)O(qlogn)
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);}
}