CF1063F String Journey 后缀自动机+线段树+DP

好神仙的一道字符串题!

由于后缀自动机+线段树合并的题做多了,看到复杂字符串的时候直接往 right 集合和后缀树那方面想了. 

所以就没想出来 QAQ....    

这道题还是要从序列上来思考.  

我们发现最优解一定可以表示成一个长度依次为 $1$ ~ $ans$ 字符串集合.   

  • 令 $dp[i]$ 表示以 $i$ 开头的答案.   
  • 那么有 $dp[i] \leqslant dp[i+1]+1$   
  • $dp[i]=dp[j]+1$,其中 $dp[j]$ 是最大满足可以转移的.  
  • 由 $dp[i] \leqslant dp[i+1]+1$,我们可以直接枚举 $dp$ 值并验证.   
  • 验证的话需要满足 $i$ 开头的字符串是某个 $j$ 串的前缀或后缀.     
  • 由于满足 $dp[i] \leqslant dp[j]+1$,所以当 $dp[i]$ 减小时可提供贡献的 $j$ 也相应减小,类加进来就行.   
  • 统计的话用一个倍增 + 线段树维护后缀树的 dfs 序即可.  

code:

#include <cstdio> 
#include <cstring> 
#include <algorithm>  
#define N 1000006      
#define lson now<<1 
#define rson now<<1|1   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;       
char str[N];  
int last=1,tot=1,tim,edges,n;    
int f[21][N],ep[N],dp[N];   
int hd[N],to[N],nex[N],maxv[N<<2];  
int ch[N][26],pre[N],mx[N],pos[N],dfn[N];      
void add(int u,int v) 
{
	nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;  
}  
void update(int l,int r,int now,int p,int v) 
{     
	maxv[now]=max(maxv[now],v);    
	if(l==r) 
		return;   
	int mid=(l+r)>>1;  
	if(p<=mid)   
		update(l,mid,lson,p,v);  
	else 
		update(mid+1,r,rson,p,v);  
}
int query(int l,int r,int now,int L,int R) 
{
	if(R<l||L>r||L>R) 
		return 0;  
	if(l>=L&&r<=R)   
		return maxv[now];  
	int mid=(l+r)>>1,re=0;    
	if(L<=mid)   
		re=max(re,query(l,mid,lson,L,R));   
	if(R>mid)   
		re=max(re,query(mid+1,r,rson,L,R)); 
	return re;   
}
void extend(int c,int id) 
{
	int np=++tot,p=last;   
	mx[np]=mx[p]+1,last=np;   
	for(;p&&!ch[p][c];p=pre[p])   
		ch[p][c]=np;   
	if(!p) 
		pre[np]=1;  
	else 
	{
		int q=ch[p][c];  
		if(mx[q]==mx[p]+1)  
			pre[np]=q;  
		else 
		{
			int nq=++tot;  
			mx[nq]=mx[p]+1;  
			pre[nq]=pre[q],pre[np]=pre[q]=nq;    
			memcpy(ch[nq],ch[q],sizeof(ch[q]));     
			for(;p&&ch[p][c]==q;p=pre[p])   
				ch[p][c]=nq;   
		}
	}
	pos[id]=last;   
}    
void dfs(int x) 
{
	dfn[x]=++tim;     
	f[0][x]=pre[x];   
	for(int i=1;(1<<i)<=tot;++i)  
		f[i][x]=f[i-1][f[i-1][x]];       
	for(int i=hd[x];i;i=nex[i])  
		dfs(to[i]);    
	ep[x]=tim;   
}
int get_p(int x,int d) 
{
	x=pos[x];   
	for(int i=20;i>=0;--i)   
		if(f[i][x]&&mx[f[i][x]]>=d)      
			x=f[i][x];    
	return x;   
}
int check(int x,int d) 
{ 
	if(d==1)  
		return 1;      
	if(x-d+1<1)  
		return 0;    
	int p=get_p(x,d-1);   
	if(query(1,tot,1,dfn[p],ep[p])>=d-1)   
		return 1;  
	p=get_p(x-1,d-1);      
	if(query(1,tot,1,dfn[p],ep[p])>=d-1)   
		return 1;        
	return 0;  
}
int main()
{ 
	// setIO("input");  
	int i,j,ans=0;     
	scanf("%d%s",&n,str+1);   
	reverse(str+1,str+1+n);   
	for(i=1;i<=n;++i)   
		extend(str[i]-'a',i);     
	for(i=2;i<=tot;++i)   
		add(pre[i],i);   
	dfs(1);               
	for(i=1,j=0;i<=n;++i) 
	{
		dp[i]=dp[i-1]+1;   
		while(!check(i,dp[i]))  
		{     
			--dp[i],++j;   
			update(1,tot,1,dfn[get_p(j,dp[j])],dp[j]);    
		}
		ans=max(ans,dp[i]);  
	}
	printf("%d\n",ans);  
	return 0;
}

  

上一篇:1361:产生数(Produce)


下一篇:[Journey with golang] 2. Function