[JSOI2019]节日庆典(Z-algorithm)

要想让一个位置作为最小循环,其必须是最小后缀,然后一个字符串的最小后缀不超过O(logn)个,于是维护备选集合即可。

然而要在O(n)复杂度求解,需要求出原串后缀与原串的LCP长度,需要用Z-algorithm。而此时由于备选后缀存在前缀关系,比较时只需用到每个后缀与原串的LCP

#include<bits/stdc++.h>
using namespace std;
const int N=3e6+7;
int n,ans,lcp[N];
char str[N];
vector<int>f,g;
int cmp(int x,int len)
{
    if(lcp[x]>=len)return 0;
    return str[1+lcp[x]]<str[x+lcp[x]]?1:-1;
}
int main()
{
    scanf("%s",str+1),n=strlen(str+1);
    for(int i=2,l=1,r=1;i<=n;i++)
    {
        lcp[i]=r>=i?min(lcp[i-l+1],r-i+1):0;
        while(str[i+lcp[i]]==str[1+lcp[i]])lcp[i]++;
        if(i+lcp[i]-1>r)r=i+lcp[i]-1,l=i;
    }
    for(int i=1;i<=n;i++)
    {
        g.clear(),f.push_back(i);
        for(int j=0;j<f.size();j++)
        {
            while(g.size()&&str[i]<str[g.back()+i-f[j]])g.pop_back();
            if(!g.size()||str[i]==str[g.back()+i-f[j]]&&i-g.back()+1>=2*(i-f[j]+1))
            g.push_back(f[j]);
        }
        f=g;
        ans=f[0];
        for(int j=0;j<f.size();j++)
        {
            int y=f[j],t=cmp(ans+i-y+1,y-ans);
            if(t==1)ans=y;
            else if(!t&&cmp(y-ans+1,ans-1)==-1)ans=y;
        }
        printf("%d ",ans);
    }
}
View Code
原文地址:https://www.cnblogs.com/hfctf0210/p/10852838.html