


【题解】CF700E Cool Slogans
热度: loading...
串串题!
个人感觉本题和CF1063F 很像,特别是关于相邻串之间的条件,可以先去看看这题。
考虑在同样满足条件的情况下, 是 的后缀的情况一定比 不是 后缀的情况优。
证明:
如果 不是 的后缀,那么我们把 中, 第二次出现位置的结尾之后的字符全部删掉,得到 。则 和 依旧满足条件,且由于 是 的子串, 与 依旧满足条件,对整个序列的合法性不造成影响。
因此我们只考虑 是 后缀的情况。
考虑建立后缀自动机。对于后缀自动机的 parent tree 而言,每个点 的 所代表的最长串都是点 所代表的最长串的严格后缀。我们先只考虑从 向 贡献。既然 作为 的后缀,那它第二次出现的位置已经确定了,只需要考虑它是否有在前面的位置出现过即可。设点 所代表的最长串的第一次出现位置为 ,计算 的 集合是否在 中有元素即可。
我们考虑用线段树合并维护每个点的 集合。考虑一个点的 集合由自己的所有儿子的 集合合并而来,且当且仅当 该点所代表的最长串是整个串的一个前缀时 会缺少最长串第一次出现的位置,故从儿子合并上来后加入最长串第一次出现的位置即可。
现在来证明一个结论:对于一个 而言,当 的最长串符合条件时, 中的所有串符合条件。当 的最长串不符合条件时, 中的所有串都不符合条件。
证明:
首先第一点很好证,由于 中的所有其它串都是 的最长串的后缀,因此只要 的最长串符合条件其也一定符合条件。
对于第二点,我们假设 的最长串不在 的最长串中出现两次,而 所代表的另一个更小的串在 的最长串中出现了两次。由于两个串的 集合相同,因此必然可以在 的最长串的前面添加一串字符得到字符串 ,使得 所代表的的最长串在 中出现两次。由于 的最长串是 的后缀,因此 属于 的儿子。但是由于每个 的最长串都必定为一个 的后缀,所以 ,这点证明 属于 自身或 的祖先,与之前推导出的 属于 的儿子矛盾,假设不成立。
设 为考虑到 时序列的最长长度, 为考虑到 时序列结尾的位置。如果 的最长串与 满足条件,则 ,且 ,否则 , 。将后缀自动机的节点拓扑排序一下从后往前 即可。
#include<bits/stdc++.h>
#define N 600010
#define mid (l+r)/2
using namespace std;
int n;
char s[N];
int ls[N<<5],rs[N<<5],cnt,rt[N];
void update(int &k,int l,int r,int pos) {
if(!k) k=++cnt;
if(l==r) return ;
if(pos<=mid) update(ls[k],l,mid,pos);
else update(rs[k],mid+1,r,pos);
}
int merge(int x,int y,int l,int r) {
if(!x||!y) return x+y;
int now=++cnt;
if(l!=r) {
ls[now]=merge(ls[x],ls[y],l,mid);
rs[now]=merge(rs[x],rs[y],mid+1,r);
}
return now;
}
int query(int k,int l,int r,int L,int R) {
if(!k||l>R||r<L) return 0;
if(L<=l&&r<=R) return 1;
if(R<=mid) return query(ls[k],l,mid,L,R);
else if(L>mid) return query(rs[k],mid+1,r,L,R);
else return query(ls[k],l,mid,L,R)||query(rs[k],mid+1,r,L,R);
}
struct SAM {
int tr[N][30],fa[N],lst,tot,pos[N],len[N];
SAM(){lst=tot=1;}
void add(int c,int w) {
int p=lst,np=++tot;
len[np]=len[p]+1,pos[np]=w,lst=np;
update(rt[np],1,n,w);
while(p&&!tr[p][c]) tr[p][c]=np,p=fa[p];
if(!p) {fa[np]=1;return ;}
int q=tr[p][c];
if(len[p]+1==len[q]) {fa[np]=q;return ;}
int nq=++tot;
len[nq]=len[p]+1;
fa[nq]=fa[q],pos[nq]=pos[q],fa[q]=fa[np]=nq;
for(int i=0; i<26; i++) tr[nq][i]=tr[q][i];
while(p&&tr[p][c]==q) tr[p][c]=nq,p=fa[p];
}
int buc[N],rk[N];
void topo() {
for(int i=1; i<=tot; i++) buc[len[i]]++;
for(int i=1; i<=n; i++) buc[i]+=buc[i-1];
for(int i=1; i<=tot; i++) rk[buc[len[i]]--]=i;
}
}sam;
int read() {
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch)) res=res*10+ch-'0',ch=getchar();
return f*res;
}
int f[N],g[N],ans;
int main()
{
n=read();
scanf("%s",s+1);
for(int i=1; i<=n; i++) sam.add(s[i]-'a',i);
sam.topo();
for(int i=sam.tot; i>1; i--) {
int u=sam.rk[i],v=sam.fa[u];
rt[v]=merge(rt[v],rt[u],1,n);
}
for(int i=2; i<=sam.tot; i++) {
int u=sam.rk[i],v=sam.fa[u];
if(v==1) f[u]=1,g[u]=u;
else if(query(rt[g[v]],1,n,sam.pos[u]-sam.len[u]+sam.len[g[v]],sam.pos[u]-1)) f[u]=f[v]+1,g[u]=u;
else f[u]=f[v],g[u]=g[v];
ans=max(ans,f[u]);
}
printf("%d",ans);
return 0;
}
