【题解】CF700E

发布时间 2023-04-18 21:45:40作者: flywatre

Cool Slogans

给定一个字符串 \(S\),要求构造字符串序列 \(s_1,s_2,\ldots,s_k\),满足任意 \(s_i\) 都是 \(S\) 的子串,且任意 \(i\in[2,n]\),都有 \(s_{i-1}\)\(s_i\) 中出现了至少 \(2\) 次(可以有重叠部分,只要起始、结尾位置不同即可)。

求可能的最大的 \(k\) 的值(即序列的最大可能长度)。

\(n \leq 2 \times 10^5\)

题解

经典可持久化线段树合并endpos,父节点能转移到子节点即是在子节点对应的区间内能存在两个父节点的endpos,随便瞎搞一下就行。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=200010,M=400010;
int n,cnt=1;
char s[N];
int head[M],fro[M],to[M],tail;
inline void adlin(int x,int y){
	to[++tail]=y,fro[tail]=head[x],head[x]=tail;
	return ;
}
int pos[M];
struct SAM{
	int len[M],to[M][26],lin[M],last=1;
	inline void insert(int x,int pla){
		int p=last,cur=++cnt;
		len[cur]=len[p]+1,last=cur,pos[cur]=pla;
		while(p&&!to[p][x])to[p][x]=cur,p=lin[p];
		if(!p)return lin[cur]=1,void(0);
		int q=to[p][x];
		if(len[q]==len[p]+1)return lin[cur]=q,void(0);
		int clone=++cnt;
		len[clone]=len[p]+1,memcpy(to[clone],to[q],sizeof(to[clone])),lin[clone]=lin[q],pos[clone]=pos[q];
		lin[q]=lin[cur]=clone;
		while(p&&to[p][x]==q)to[p][x]=clone,p=lin[p];
		return ;
	}
}sam;
int rt[M];
struct SegTree{
	int ls[M*80],rs[M*80],tot;
	void add(int &u,int v,int l,int r,int aim){
		u=++tot;
		if(l==r)return ;
		int mid=(l+r)/2;
		if(aim<=mid)rs[u]=rs[v],add(ls[u],ls[v],l,mid,aim);
		else ls[u]=ls[v],add(rs[u],rs[v],mid+1,r,aim);
		return ;
	}
	bool get(int u,int l,int r,int L,int R){
		if(!u)return false;
		if(L<=l&&r<=R)return true;
		bool ansn=false;
		int mid=(l+r)/2;
		if(L<=mid)ansn|=get(ls[u],l,mid,L,R);
		if(R>mid)ansn|=get(rs[u],mid+1,r,L,R);
		return ansn;
	}
	void merge(int &u,int a,int b,int l,int r){
		if(!a||!b)return u=a|b,void(0);
		u=++tot;
		int mid=(l+r)/2;
		merge(ls[u],ls[a],ls[b],l,mid);
		merge(rs[u],rs[a],rs[b],mid+1,r);
		return ;
	}
	
}tree;
int f[M],g[M];
void dfs1(int u){
//	cout<<"wk:"<<u<<":"<<g[u]<<"-"<<tree.tr[rt[u]]<<"\n";
//	if(sam.len[u]==1)g[u]=1;
	for(int k=head[u];k;k=fro[k]){
		int x=to[k];
		dfs1(x);
		tree.merge(rt[u],rt[u],rt[x],1,n);
	}
	return ;
}
int ans;
void dfs2(int u){
//	cout<<"wk:"<<u<<":"<<f[u]<<" "<<g[u]<<"-"<<tree.tr[rt[u]]<<":"<<sam.len[u]<<"\n";
	ans=max(f[u],ans);
	for(int k=head[u];k;k=fro[k]){
		int x=to[k];
		if(tree.get(rt[g[u]],1,n,pos[x]-sam.len[x]+sam.len[g[u]],pos[x]-1)){
			f[x]=f[u]+1,g[x]=x;
		}
		else{
			f[x]=f[u],g[x]=g[u];
		}
		if(sam.len[x]==1)f[x]=1,g[x]=x;
//		cout<<u<<"->"<<x<<":"<<pos[x]<<" "<<f[x]<<"-"<<pos[g[x]]<<"-"<<sam.len[x]<<" "<<sam.len[g[u]]<<"\n";
		dfs2(x);
	}
	return ;
}
signed main(){
	n=rd(),scanf("%s",s+1);
	for(int i=1;i<=n;i++)sam.insert(s[i]-'a',i);
//	for(int i=1;i<=cnt;i++)cout<<sam.lin[i]<<"->"<<i<<":"<<pos[i]<<"\n";
	for(int i=cnt;i>=2;i--)adlin(sam.lin[i],i),g[i]=1;
	for(int i=2;i<=cnt;i++)tree.add(rt[i],rt[i],1,n,pos[i]);
	dfs1(1),dfs2(1);
	printf("%d\n",ans);
	return 0;
}
/*
7
daddddd

21
abcaaaaabbcbacbabcbcb
*/