题解 肃正协议
考场思路是将fail树加入sam形成的DAG中,这样一个串是另一个串的条件是可达另一个串
但复杂度是 \(O(n^2\sqrt n)\) 的
发现首先可以钦定选中的最短的串长度为1,且所有选中的串长度恰好相差1
于是考虑DP,令 \(dp_i\) 为恰好以点 \(i\) 为起点时选的串数的最大值(同时也是以 \(i\) 为起点的那个串的长度)
发现可以二分
但其实可以发现 \(dp_i\leqslant dp_{i+1}+1\)
- 注意找形如「相邻的DP值相差不超过1」的限制,有时可以去掉二分的log
于是每个 \(dp_i\) 从 \(dp_{i+1}+1\) 开始向下爆扫即可(下标每减少1最多增加1的势能)
考虑怎么check一个 \(dp_i\) 是否合法
有一个 \(O(n\sqrt n)\) 的暴力做法是依据有用的区间长度不超过根号,所以可以预处理所有合法区间的hash值
然后每次就要在 \([i+dp_i, n]\) 中查询一个DP值是否出现过
发现这个 \(i+dp_i\) 单调不升,所以对这个东西维护一个指针即可
然后考虑优化
发现我们实际上是要找到一个点 \(j\) 使 \(i+dp_i<j-dp_j+1\) 且有 \(s_{i, i+dp_i-2}=s_{j-dp_j+1}\) 或 \(s_{i+1, i+dp_j-1}=s_{j-dp_j+1}\)
还要求 \(dp_j\geqslant dp_i-1\)
首先 \(i+dp_i\) 不增,前一个条件还是可以单调指针解决
那后面两个条件发现放到sam上就是查询两个状态的fail树子树中的最大值
- 给定一个串,多次询问,每次给定一个区间 \([l, r]\),要求关于快速找到sam上 \(s_{l, r}\) 对应的子串对应的状态:
将原串插入sam时记录 \(pos_i\) 为第 \(i\) 个前缀的位置
将问题转化为从 \(pos_r\) 向上跳到一个 \(len=r-l+1\) 的节点
于是在fail树上倍增,跳跃的条件是fa[j][u] && len[fa[j][u]]>=r-l+1
然后这题卡常,需要zkw线段树
本来准备学一下的结果写了个拍把网上的大部分板子(包括skyh的)都hack了又懒得自己再总结一个板子了于是自闭
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define ll long long
#define fir first
#define sec second
#define ull unsigned long long
//#define int long long
int n;
char s[N];
// namespace force{
// const ull base=13131;
// ll h[N], p[N];
// int ans;
// inline ull hashing(int l, int r) {return h[r]-h[l-1]*p[r-l+1];}
// unordered_map<ull, int> mp[N];
// void solve() {
// p[0]=1;
// for (int i=1; i<=n; ++i) p[i]=p[i-1]*base;
// for (int i=1; i<=n; ++i) h[i]=h[i-1]*base+s[i];
// for (int i=n; i; --i) {
// for (int j=i; j<=n; ++j) {
// if (clock()>900000) {printf("%d\n", ans); exit(0);}
// ull t=hashing(i, j);
// int now=1;
// for (int k=i; k<=j; ++k) {
// for (int l=k; l<=j; ++l) if (!(k==i&&l==j)) {
// ull t2=hashing(k, l);
// int tem=0;
// for (int v=j+1; v<=n; ++v)
// if (mp[v].find(t2)!=mp[v].end())
// tem=max(tem, mp[v][t2]);
// now=max(now, tem+1);
// }
// }
// mp[i][t]=max(mp[i][t], now);
// ans=max(ans, now);
// }
// }
// printf("%d\n", ans);
// exit(0);
// }
// }
// namespace task1{
// int rot[N], ans;
// namespace sam{
// int head[N], size;
// int len[N<<1], fail[N<<1], tr[N<<1][26], now, tot;
// map<int, int> dp[N<<1];
// set<int> st[N<<1];
// struct edge{int to, next;}e[N<<1];
// inline void add(int s, int t) {e[++size]={t, head[s]}; head[s]=size;}
// void init() {now=tot=size=0; memset(head, -1, sizeof(head)); fail[0]=-1;}
// void insert(char c, int pos) {
// c-='a';
// int cur=++tot;
// len[cur]=len[now]+1;
// int p, q;
// for (p=now; ~p&&!tr[p][c]; tr[p][c]=cur,p=fail[p]);
// if (p==-1) fail[cur]=0;
// else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
// else {
// int cln=++tot;
// len[cln]=len[p]+1;
// fail[cln]=fail[q];
// for (int i=0; i<26; ++i) tr[cln][i]=tr[q][i];
// for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
// fail[cur]=fail[q]=cln;
// }
// now=cur;
// // seg::upd(rot[cur], 1, n, i, 1);
// st[cur].insert(pos);
// }
// void dfs(int u) {
// set<int>::iterator tem;
// if (st[u].size()) dp[u][1]=*st[u].begin(); //, cout<<"add: "<<(*st[u].begin())<<endl;
// for (int i=head[u],v; ~i; i=e[i].next) {
// v = e[i].to;
// dfs(v);
// // seg::merge(rot[u], rot[v], 1, n);
// if (st[v].size()>st[u].size()) swap(st[u], st[v]);
// for (auto it:st[v]) st[u].insert(it);
// }
// cout<<"st: "<<u<<endl;
// for (auto it:st[u]) cout<<it<<' '; cout<<endl;
// if (!u) return ;
// for (int k=len[u]; k>len[fail[u]]; --k) {
// for (int i=head[u],v; ~i; i=e[i].next) {
// v = e[i].to;
// for (auto it:dp[v]) {
// if (it.sec+k<=n && (tem=st[u].lower_bound(it.sec+k))!=st[u].end()) {
// // cout<<"tem: "<<*tem<<endl;
// if (dp[u].find(it.fir+1)==dp[u].end()) dp[u][it.fir+1]=*tem;
// else dp[u][it.fir+1]=min(dp[u][it.fir+1], *tem);
// }
// if (dp[u].find(it.fir)==dp[u].end()) dp[u][it.fir]=it.sec;
// else dp[u][it.fir]=min(dp[u][it.fir], it.sec);
// }
// }
// }
// cout<<"dp: "<<u<<endl;
// for (auto it:dp[u]) cout<<it.fir<<','<<it.sec<<' '; cout<<endl;
// for (int k=len[u]; k>len[fail[u]]+1; --k) {
// for (auto it:dp[u]) {
// if (it.sec+(k-1)<=n && (tem=st[u].lower_bound(it.sec+(k-1)))!=st[u].end()) {
// if (dp[u].find(it.fir+1)==dp[u].end()) dp[u][it.fir+1]=*tem;
// else dp[u][it.fir+1]=min(dp[u][it.fir+1], *tem);
// }
// }
// }
// }
// }
// void solve() {
// using namespace sam;
// init();
// for (int i=1; i<=n; ++i) insert(s[i], i);
// for (int i=1; i<=tot; ++i) add(fail[i], i);
// dfs(0);
// for (int i=head[0],u; ~i; i=e[i].next) {
// u = e[i].to;
// for (auto it:dp[u]) ans=max(ans, it.fir);
// }
// printf("%d\n", ans);
// exit(0);
// }
// }
namespace task{
int head[N<<1], in[N<<1], siz[N<<1], lg[N<<1], dep[N<<1], size;
int dp[N], pos[N], deg[N<<1], tem[N<<1], fa[20][N], ans=1;
int len[N<<1], fail[N<<1], tr[N<<1][26], now, tot, ord;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++size]={t, head[s]}; head[s]=size;}
namespace sam{
void init() {now=tot=size=0; memset(head, -1, sizeof(head)); fail[0]=-1;}
void insert(char c, int i) {
c-='a';
int cur=++tot;
len[cur]=len[now]+1;
int p, q;
for (p=now; ~p&&!tr[p][c]; tr[p][c]=cur,p=fail[p]);
if (p==-1) fail[cur]=0;
else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
else {
int cln=++tot;
len[cln]=len[p]+1;
fail[cln]=fail[q];
for (int i=0; i<26; ++i) tr[cln][i]=tr[q][i];
for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
fail[cur]=fail[q]=cln;
}
now=cur;
pos[i]=cur;
}
void dfs(int u) {
siz[u]=1; in[u]=++ord;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
fa[0][v]=u; dep[v]=dep[u]+1;
dfs(v);
siz[u]+=siz[v];
}
}
}
// namespace zkw{
// int tl[N<<2], tr[N<<2], dat[N<<2];
// #define tl(p) tl[p]
// #define tr(p) tr[p]
// #define dat(p) dat[p]
// #define pushup(p) dat(p)=max(dat(p<<1), dat(p<<1|1))
// void build(int p, int l, int r) {
// tl(p)=l; tr(p)=r; dat(p)=0;
// if (l==r) return ;
// int mid=(l+r)>>1;
// build(p<<1, l, mid);
// build(p<<1|1, mid+1, r);
// pushup(p);
// }
// void upd(int p, int pos, int val) {
// if (tl(p)==tr(p)) {dat(p)=val; return ;}
// int mid=(tl(p)+tr(p))>>1;
// if (pos<=mid) upd(p<<1, pos, val);
// else upd(p<<1|1, pos, val);
// pushup(p);
// }
// int query(int p, int l, int r) {
// if (l<=tl(p)&&r>=tr(p)) return dat(p);
// int mid=(tl(p)+tr(p))>>1, ans=-1;
// if (l<=mid) ans=max(ans, query(p<<1, l, r));
// if (r>mid) ans=max(ans, query(p<<1|1, l, r));
// return ans;
// }
// }
// namespace zkw{
// int M, sum[N<<2], maxn[N<<2];
// void build(int p, int l, int n) {
// for (M=1; M<=n+1; M<<=1);
// // for (int i=M-1; i; --i) {
// // sum[i]=sum[i<<1]+sum[i<<1|1];
// // maxn[i]=max(maxn[i<<1], maxn[i<<1|1]);
// // maxn[i<<1]-=maxn[i], maxn[i<<1|1]-=maxn[i];
// // }
// }
// void upd(int p, int x, int val) {
// int k;
// for (x+=M; x; x>>=1) {
// sum[x]+=val; maxn[x]+=val;
// k=max(maxn[x], maxn[x^1]);
// maxn[x]-=k; maxn[x^1]-=k; maxn[x>>1]+=k;
// }
// }
// int query(int p) {
// int ans=0;
// for (p+=M,ans=sum[p],p>>=1; p; p>>=1) ans+=0;
// return ans;
// }
// int query(int p, int l, int r) {
// // assert(l);
// if (l==r) return query(l);
// int L=0, R=0;
// for (l+=M,r+=M; l^r^1; l>>=1,r>>=1) {
// L+=maxn[l], R+=maxn[r];
// if (~l&1) L=max(L, maxn[l^1]);
// if (r&1) R=max(R, maxn[r^1]);
// }
// int ans=max(L+maxn[l], R+maxn[r]);
// for (l>>=1; l; l>>=1) ans+=maxn[l];
// return ans;
// }
// }
// namespace zkw{
// int n,bit;
// int sum[N<<2],add[N<<2],mn[N<<2],mx[N<<2];
// inline void build(int a1, int a2, int t){
// n=t;
// for(bit=1;bit<=n+1;bit<<=1);
// for(int i=bit+1;i<=bit+n;++i) mx[i]=mn[i]=sum[i]=0;
// for(int i=bit-1;i;--i){
// sum[i]=sum[i<<1]+sum[i<<1|1];
// mn[i]=min(mn[i<<1],mn[i<<1|1]); mn[i<<1]-=mn[i]; mn[i<<1|1]-=mn[i];
// mx[i]=max(mx[i<<1],mx[i<<1|1]); mx[i<<1]-=mx[i]; mx[i<<1|1]-=mx[i];
// }
// }
// inline int query(int p){
// int ans=0;
// for(p+=bit,ans=sum[p],p>>=1;p;p>>=1) ans+=add[p];
// return ans;
// }
// inline int query_sum(int t, int l,int r){
// int ans=0,lc=0,rc=0,len=1;
// for(l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1,len<<=1){
// ans+=add[l]*lc+add[r]*rc;
// if(~l&1) ans+=sum[l^1],lc+=len;
// if(r&1) ans+=sum[r^1],rc+=len;
// }
// for(;l;l>>=1,r>>=1) ans+=add[l]*lc+add[r]*rc;
// return ans;
// }
// inline int query_min(int t, int l,int r){
// if(l==r) return query(l);
// int lans=0,rans=0;
// for(l+=bit,r+=bit;l^r^1;l>>=1,r>>=1){
// lans+=mn[l]; rans+=mn[r];
// if(~l&1) lans=min(lans,mn[l^1]);
// if(r&1) rans=min(rans,mn[r^1]);
// }
// for(lans=min(lans+mn[l],rans+mn[r]),l>>=1;l;l>>=1) lans+=mn[l];
// return lans;
// }
// inline int query(int t, int l,int r){
// if(l==r) return query(l);
// int lans=0,rans=0;
// for(l+=bit,r+=bit;l^r^1;l>>=1,r>>=1){
// lans+=mx[l]; rans+=mx[r];
// if(~l&1) lans=max(lans,mx[l^1]);
// if(r&1) rans=max(rans,mx[r^1]);
// }
// for(lans=max(lans+mx[l],rans+mx[r]),l>>=1;l;l>>=1) lans+=mx[l];
// return lans;
// }
// inline void modify(int t, int l,int r,int val){
// int lc=0,rc=0,len=1,x;
// for(l+=bit-1,r+=bit+1;l^r^1;l>>=1,r>>=1,len<<=1){
// sum[l]+=lc*val; sum[r]+=rc*val;
// if(~l&1) sum[l^1]+=len*val,add[l^1]+=val,mn[l^1]+=val,lc+=len;
// if(r&1) sum[r^1]+=len*val,add[r^1]+=val,mn[r^1]+=val,rc+=len;
// x=min(mn[l],mn[l^1]); mn[l]-=x; mn[l^1]-=x; mn[l>>1]+=x;
// x=min(mn[r],mn[r^1]); mn[r]-=x; mn[r^1]-=x; mn[r>>1]+=x;
// }
// for(;l;l>>=1,r>>=1){
// sum[l]+=lc*val; sum[r]+=rc*val;
// x=min(mn[l],mn[l^1]); mn[l]-=x; mn[l^1]-=x; mn[l>>1]+=x;
// x=max(mx[l],mx[l^1]); mx[l]-=x; mx[l^1]-=x; mx[l>>1]+=x;
// }
// }
// inline void upd(int t, int p,int val){
// int x;
// for(p+=bit;p;p>>=1){
// sum[p]+=val; mn[p]+=val; mx[p]+=val;
// x=min(mn[p],mn[p^1]); mn[p]-=x; mn[p^1]-=x; mn[p>>1]+=x;
// x=max(mx[p],mx[p^1]); mx[p]-=x; mx[p^1]-=x; mx[p>>1]+=x;
// }
// }
// }
namespace zkw{
int M, mx[N<<2];
void build(int a, int b, int n) {for (M=1; M<=n; M<<=1);}
void upd(int t, int x, int v) {
x+=M;
for (; x; x>>=1) mx[x]=max(mx[x], v);
}
int query(int t, int l, int r) {
int ans=0;
for (l+=M-1,r+=M+1; l^r^1; l>>=1,r>>=1) {
if (~l&1) ans=max(ans, mx[l+1]);
if (r&1) ans=max(ans, mx[r-1]);
}
return ans;
}
}
int anc(int u, int k) {
for (int j=lg[dep[u]]-1; ~j; --j)
if (fa[j][u] && len[fa[j][u]]>=k)
u=fa[j][u];
return u;
}
bool check(int i, int l) {
// cout<<"check: "<<i<<' '<<l<<endl;
int t1=anc(pos[i], l-1), t2=anc(pos[i-1], l-1);
// cout<<"pos: "<<pos[i]<<' '<<pos[i-1]<<endl;
// cout<<"t: "<<t1<<' '<<t2<<endl;
// cout<<"in: "<<in[t1]<<' '<<in[t1]+siz[t1]-1<<' '<<seg::query(1, in[t1], in[t1]+siz[t1]-1)<<endl;
// cout<<"in: "<<in[t2]<<' '<<in[t2]+siz[t2]-1<<' '<<seg::query(1, in[t2], in[t2]+siz[t2]-1)<<endl;
if (zkw::query(1, in[t1], in[t1]+siz[t1]-1)>=l-1) return 1;
if (zkw::query(1, in[t2], in[t2]+siz[t2]-1)>=l-1) return 1;
return 0;
}
void solve() {
sam::init();
reverse(s+1, s+n+1);
for (int i=1; i<=n; ++i) sam::insert(s[i], i);
for (int i=1; i<=tot; ++i) add(fail[i], i); //, cout<<"add: "<<fail[i]<<' '<<i<<endl;
for (int i=1; i<=tot; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
sam::dfs(0);
zkw::build(1, 1, tot+1);
// cout<<"build: "<<1<<' '<<tot<<endl;
for (int i=1; i<=tot; ++i) ++deg[len[i]];
for (int i=1; i<=tot; ++i) deg[i]+=deg[i-1];
for (int i=1; i<=tot; ++i) tem[deg[len[i]]--]=i;
for (int i=1,t=tem[1]; i<=tot; t=tem[++i])
for (int j=1; j<=20; ++j)
if (!(fa[j][t]=fa[j-1][fa[j-1][t]]))
break;
int lim=1;
dp[1]=1;
for (int i=2; i<=n; ++i) {
dp[i]=dp[i-1]+1;
while (!check(i, dp[i])) {
--dp[i];
for (; lim<=i-dp[i]; ++lim) zkw::upd(1, in[pos[lim]], dp[lim]); //, cout<<"upd: "<<lim<<' '<<pos[lim]<<' '<<in[pos[lim]]<<' '<<dp[lim]<<endl;
}
for (; lim<=i-dp[i]; ++lim) zkw::upd(1, in[pos[lim]], dp[lim]); //, cout<<"upd: "<<lim<<' '<<pos[lim]<<' '<<in[pos[lim]]<<' '<<dp[lim]<<endl;
ans=max(ans, dp[i]);
}
// cout<<"pos: "; for (int i=1; i<=n; ++i) cout<<pos[i]<<' '; cout<<endl;
// cout<<"dp: "; for (int i=1; i<=n; ++i) cout<<dp[i]<<' '; cout<<endl;
printf("%d\n", ans);
exit(0);
}
}
signed main()
{
scanf("%s", s+1);
n=strlen(s+1);
// force::solve();
task::solve();
return 0;
}