题解:P12923 [POI 2021/2022 R3] 模板 2 / Szablon 2
想了一通没想到就是最暴力的做法。
对于每个长度
这样做复杂度比想象中的要好的多。考虑每次向后拓展的长度。若连续两次拓展长度
那不如直接用最后一条去拓展,并且是可行的。也就是说暴力拓展的次数是
剩下的就是怎么找到最右边的拓展位置。正着和反着割跑一遍 exKMP,就可以找出每个位置开头的正串和反串与
从小到大枚举
由于不好按秩合并,复杂度没变,但常数要小很多。
#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N=1e6+5;
int n,z[N],rz[N];
vector<int> d[N],rd[N];
char s[N],t[N];
struct UU{
int n,fa[N];
void init(int N){n=N;
for(int i=0;i<=n;++i)fa[i]=i;
}int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}void del(int x){
fa[x]=find(x-1);
}int ask(int x){return find(min(x,n));}
}f,rf;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>s+1,n=strlen(s+1);
for(int i=1;i<=n;++i)
t[i]=s[n-i+1];
z[1]=n;
for(int i=2,l=0,r=0;i<=n;++i){
if(i<=r)z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&s[i+z[i]]==s[z[i]+1])++z[i];
if(i+z[i]-1>r)l=i,r=i+z[i]-1;
}for(int i=1,l=0,r=0;i<=n;++i){
if(i<=r)rz[i]=min(z[i-l+1],r-i+1);
while(i+rz[i]<=n&&t[i+rz[i]]==s[rz[i]+1])++rz[i];
if(i+rz[i]-1>r)l=i,r=i+rz[i]-1;
}reverse(rz+1,rz+n+1);
// for(int i=1;i<=n;++i)
// cout<<z[i]<<' ';
// cout<<'\n';
// for(int i=1;i<=n;++i)
// cout<<rz[i]<<' ';
// cout<<'\n';
f.init(n),rf.init(n);
for(int i=1;i<=n;++i)
d[z[i]+1].eb(i),rd[rz[i]+1].eb(i);
for(int i=1;i<=n;++i){
for(int v:d[i])f.del(v);
for(int v:rd[i])rf.del(v);
int p=0;
while(1){
int q=max(f.ask(p+1)+i-1,rf.ask(p+i));
if(q<=p)break;
if(q>=n){cout<<i<<' ';break;}
p=q;
}
}cout<<'\n';
return 0;
}