CF700E Cool Slogans
传送门
首先,我们可以强制
所以我们选的
对于节点
u 和节点v 满足u 是v 的祖先,若取出v 中最长的串,则u 中所有的串在此串中出现的位置是一样的。
神 iostream 给出了严谨的证明,所以我这里给出一个可能会形象具体的,方便大家感性理解。
如图,线段代表的是
蓝线代表那个较长的串,红线代表较短的,但是可以发现每一个线段前面都有一小段一样的蓝的,把那一小段蓝的加进线段里也会形成一个和
我们选的那条链,深度最大的那个节点肯定贪心的选最长的,而深度第二大的因为深度最大的选了最长,根据引理,它选什么长度要么都可以要么都不行,所以它也应该贪心地选最长的......所以对于所有选的节点,我们都应该选最长。
接下来就可以直接 DP 了,
至于判断是否出现了 2 遍,套路的线段树合并维护 endpos 即可。
#include<iostream>
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#define N 400005
using namespace std;
inline int read(){
int x=0,f=0; char ch=getchar();
while(!isdigit(ch)) f|=(ch==45),ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
struct edge{
int b,n;
}e[2*N];
int n,h[2*N],tot;
char s[N];
inline void charu(int a,int b){
e[++tot].b=b,e[tot].n=h[a],h[a]=tot;
}
struct SuffixAutoMaton{
int tail,pool,pos[2*N],nxt[2*N][26],fail[2*N],len[2*N];
inline void init(){tail=pool=1,fail[1]=0;}
inline void insert(int c){
len[++pool]=len[tail]+1,pos[pool]=len[pool];
int p=tail;tail=pool;
for(;p && !nxt[p][c];p=fail[p]) nxt[p][c]=tail;
if(!p) return (void)(fail[tail]=1);
int q=nxt[p][c];
if(len[p]+1==len[q]) return (void)(fail[tail]=q);
len[++pool]=len[p]+1,fail[pool]=fail[q],pos[pool]=pos[q];
memcpy(nxt[pool],nxt[q],sizeof(nxt[q]));
fail[q]=fail[tail]=pool;
for(;nxt[p][c]==q;p=fail[p]) nxt[p][c]=pool;
}
}sam;
struct segmentTree{
int ls,rs,sum;
}d[50*N];
int nd,rt[N*2];
void update(int &k,int l,int r,int x){
k=++nd;
if(l==r) return (void)(d[k].sum=1);
int mid=l+r>>1;
if(x<=mid) update(d[k].ls,l,mid,x);
else update(d[k].rs,mid+1,r,x);
d[k].sum=d[d[k].ls].sum+d[d[k].rs].sum;
}
int merge(int x,int y){
if(!x || !y) return x|y;
int k=++nd;
d[k].sum=d[x].sum+d[y].sum;
d[k].ls=merge(d[x].ls,d[y].ls);
d[k].rs=merge(d[x].rs,d[y].rs);
return k;
}
int query(int k,int l,int r,int x,int y){
if(x<=l && r<=y) return d[k].sum;
int mid=l+r>>1,res=0;
if(x<=mid) res=query(d[k].ls,l,mid,x,y);
if(mid+1<=y) res+=query(d[k].rs,mid+1,r,x,y);
return res;
}
void dfs(int u){
if(sam.pos[u]) update(rt[u],1,n,sam.pos[u]);
for(int i=h[u];i;i=e[i].n){
int v=e[i].b;
dfs(v);
rt[u]=merge(rt[u],rt[v]);
}
}
int f[2*N],ans,lnk[2*N];
void solve(int u,int fa){
if(u!=1){
int p=lnk[fa];f[u]=f[p];
if(p==1) f[u]++;
else if(query(rt[p],1,n,sam.pos[u]-sam.len[u]+sam.len[p],sam.pos[u]-1)) f[u]++;
}
lnk[u]=(f[u]==f[fa]?lnk[fa]:u);
lnk[1]=1;
for(int i=h[u];i;i=e[i].n){
int v=e[i].b;
solve(v,u);
}
ans=max(ans,f[u]);
}
int main(){
n=read();
scanf("%s",s+1);
sam.init();
for(int i=1;i<=n;++i) sam.insert(s[i]-'a');
for(int i=1;i<=sam.pool;++i) charu(sam.fail[i],i);
dfs(1);
solve(1,0);
cout<<ans;
return 0;
}