[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); } }