codeforces 700E

题目链接

题意

给一个字符串S,要求构造字符串序列 s 1 , s 2 , . . . , s k s_1,s_2,...,s_k ,满足任意s都是S的子串,且任意i∈[2,n],都有 s i s_i s i 1 s_{i-1} 里出现了至少2次,问k最大可以是多少。

数据范围

S 200000 |S|\le 200000

解法

SAM+线段树合并

考虑先对原串建出SAM,然后考虑关于 s i , s i 1 s_i,s_{i-1} s i 1 s_{i-1} 的endpos集合一定被 s i s_i 的endpos集合包含,所以如果可以维护每个点的endpos集合,那么就可以考虑在SAM上dp。维护endpos集合可以使用线段树合并的方式,然后考虑求出答案,我们在parent树上dfs,对于一个点A,设它的一个儿子为B,如果[pos[B]-len[B]+len[A],pos[B]]中有至少2个元素在endpos(A)集合中,这就可以转移。然后又因为父亲表示的串是儿子的严格后缀,所以只要在[pos[B]-len[B]+len[A],pos[B]-1]中有一个出现的串就可以了。这个用线段树区间查询。
然后是dp本身,考虑f[u]表示u号节点的答案最多是多少,如果可以从父亲转移过来,那么f[u]=f[fa]+1,top[u]=u,如果不能,那么f[u]=f[fa],top[u]=top[fa],注意要特判父亲节点为1的情况,因为此时父亲节点是虚拟节点(我的程序从头到位没有对1号节点进行过处理):f[u]=1,top[u]=u。
这里的top表示的是最后一个被选上的有效节点是哪一个,这里可以看出这个节点的长度越短越好,所以是从较短的转移向较长的。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n;
char s[maxn];
int ch[maxn][26],len[maxn],fa[maxn],sz[maxn],pos[maxn],ed,ct;
int rt[maxn],t[maxn*20];
inline void insert(int c,int i){
	int p=ed,np=++ct;ed=ct;
	len[np]=len[p]+1;pos[np]=i;
	for(;p&&(!ch[p][c]);p=fa[p])ch[p][c]=np;
	if(!p)fa[np]=1;
	else{
		int q=ch[p][c];
		if(len[q]==len[p]+1){fa[np]=q;}
		else{
			int nq=++ct;len[nq]=len[p]+1;pos[nq]=pos[q];
			fa[nq]=fa[q];fa[q]=fa[np]=nq;
			for(int i=0;i<26;i++)ch[nq][i]=ch[q][i];
			for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
		}
	}
}
int L[maxn*20],R[maxn*20],tot;
#define ls L[rt]
#define rs R[rt]
void modify(int &rt,int l,int r,int val){
	rt=++tot;
	if(l==r)return ;
	int mid=(l+r)>>1;
	if(val<=mid)modify(ls,l,mid,val);
	else modify(rs,mid+1,r,val);
}
vector<int> son[maxn];
inline int merge(int x,int y){
	if(!x||!y)return x^y;
	int z=++tot;
	L[z]=merge(L[x],L[y]);
	R[z]=merge(R[x],R[y]);
	return z;
}
void dfs(int u){
	for(int i=0;i<son[u].size();i++){
		int v=son[u][i];
		dfs(v);
		rt[u]=merge(rt[u],rt[v]);
	}
}
int dp[maxn],top[maxn];
inline int query(int rt,int l,int r,int x,int y){
	if(!rt)return 0;if(x<=l&&r<=y)return 1;
	int mid=l+r>>1;
	if(x<=mid&&query(ls,l,mid,x,y))return 1;
	if(y>mid&&query(rs,mid+1,r,x,y))return 1;
	return 0;
}
int ans;
void dfs2(int u){
	//printf("%d\n",u);
	int ff=fa[u];
	if(ff==1){dp[u]=1;top[u]=u;}
	else{
		int x=query(rt[top[ff]],1,n,pos[u]-len[u]+len[top[ff]],pos[u]-1);
		if(x)dp[u]=dp[ff]+1,top[u]=u;
		else dp[u]=dp[ff],top[u]=top[ff];
	}
	ans=max(ans,dp[u]);
	for(int i=0;i<son[u].size();i++)dfs2(son[u][i]);
}
int main(){
	//freopen("700E.in","r",stdin);
	//freopen("700E.out","w",stdout);
	n=read();
	ed=ct=1;
	scanf("%s",s+1);
	for(int i=1;i<=n;i++){
		insert(s[i]-'a',i);
		modify(rt[ed],1,n,i);
	}
	for(int i=2;i<=ct;i++){
		son[fa[i]].push_back(i);
	}
	dfs(1);
	for(int i=0;i<son[1].size();i++){
		int v=son[1][i];
		dfs2(v);
	}
	/*for(int i=2;i<=ct;i++)
		printf("%d\n",dp[i]);*/
	printf("%d\n",ans);
	return 0;
}


发布了95 篇原创文章 · 获赞 9 · 访问量 3203

猜你喜欢

转载自blog.csdn.net/wmhtxdy/article/details/103978038