【字符串哈希】洛谷P3538 [POI2012]OKR-A Horrible Poem

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82389514

链接

https://www.luogu.org/problemnew/show/P3538


大意

给定一个长度为 n 的字符串, q 次询问,每次询问 l ~ r 之间的最短循环节


思路

首先循环节一定是这个数的约数,为了避免超时,我们可以用筛法筛出所有数的最小质因子,然后对每次询问的长度进行分解,这样就可以节省循环

判断是否为循环节的时候可以用 h a s h


代码

#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);//输出
    }
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82389514