版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82389514
链接
https://www.luogu.org/problemnew/show/P3538
大意
给定一个长度为 的字符串, 次询问,每次询问 ~ 之间的最短循环节
思路
首先循环节一定是这个数的约数,为了避免超时,我们可以用筛法筛出所有数的最小质因子,然后对每次询问的长度进行分解,这样就可以节省循环
判断是否为循环节的时候可以用
代码
#include<cstdio>
#include<cstring>
using namespace std;int n,m,l,r,k;
char s[500001];
typedef unsigned long long ull;
ull s1[500001],ss[500001],base=233;
int sushu[500001],nxt[500001],ys[500001];
bool use[500001];
inline void prime()
{
for(register int i=2;i<=500000;i++)
{
if(!use[i])
{
sushu[++k]=i;
nxt[i]=i;
}
for(register int j=1;j<=k&&(long long)i*sushu[j]<=500000;j++)
{
use[i*sushu[j]]=1;
nxt[i*sushu[j]]=sushu[j];
if(!(i%sushu[j])) break;
}
}
return;
}
signed main()
{
scanf("%d",&n);
scanf("%s",s+1);
scanf("%d",&m);
ss[0]=1;
for(register int i=1;i<=n;i++) s1[i]=s1[i-1]*base+s[i]-'a'+1,ss[i]=ss[i-1]*base;//ss为base的任意次方,s1为哈希值,这里采用的是自然溢出法
prime();//筛取
while(m--)
{
scanf("%d%d",&l,&r);//输入询问的区间
int len=r-l+1,leny=0;//计算长度同时初始化
while(len!=1)
{
ys[++leny]=nxt[len];//分解
len/=nxt[len];
}
len=r-l+1;//记得要重新赋值
for(register int j=1;j<=leny;j++)
{
int t=len/ys[j];//长度
if(s1[r-t]-ss[r-t-l+1]*s1[l-1]==s1[r]-ss[r-(l+t)+1]*s1[l+t-1]) len=t;//若该长度能组成循环节,则保存更优的
}
printf("%d\n",len);//输出
}
}