考虑一个后缀
S
i
.
.
.
n
S_{i...n}
Si...n,如果加上一个任意字符
c
c
c 可以使得
S
i
.
.
.
n
c
S_{i...n}c
Si...nc 为字典序最小的后缀,那么将其称为好后缀,跟
Z
J
O
I
2017
ZJOI2017
ZJOI2017 的结论一样,好后缀的集合大小是
log
∣
S
∣
\log |S|
log∣S∣ 的,并且好后缀的长度每次至少翻倍,证明:ZJOI2017字符串
于是暴力维护这个集合,只需要考虑比较大小,发现每次需要比较一个后缀和从 1 开始的后缀大小,用
Z
−
a
l
g
o
r
i
t
h
m
Z-algorithm
Z−algorithm 可以
O
(
1
)
O(1)
O(1) 求
L
C
P
LCP
LCP,复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
#include<bits/stdc++.h>#define cs const#define pb push_backusingnamespace std;
cs int N =3e6+50;int n;char S[N];intmain(){#ifdef FSYolandafreopen("1.in","r",stdin);#endifscanf("%s",S+1); n=strlen(S+1);staticint lcp[N];int mx=1, pt=1; lcp[1]=n;for(int i=2; i<=n; i++){if(i<=mx) lcp[i]=min(lcp[i-pt+1],mx-i+1);while(i+lcp[i]<=n && S[i+lcp[i]]==S[1+lcp[i]])++lcp[i];if(i+lcp[i]-1>mx) pt=i, mx=i+lcp[i]-1;}
vector<int> St;for(int i=1; i<=n; i++){
St.pb(i); vector<int> nw;for(auto t : St){bool ok =true;while(!nw.empty()){int x = nw.back();if(S[i]>S[i-t+x]) ok =false;if(S[i]>=S[i-t+x])break; nw.pop_back();}if(ok&&(nw.empty()||(i-t<=t-nw.back()))) nw.pb(t);} St = nw;int as = St[0];for(int j=1,t; j<St.size(); j++){
t = St[j];int pt = as + i - t +1;bool ok =false;if(pt+lcp[pt]<=i) ok|=S[1+lcp[pt]]<S[pt+lcp[pt]];else{int x=1+i-pt+1;if(x+lcp[x]<=i) ok|=S[x+lcp[x]]<S[1+lcp[x]];}if(ok) as = t;} cout << as <<" ";}return0;}