【BJWC2018】Border 的四种求法(SAM)(线段树合并)(链分治)(DSU on Tree)

传送门

题意:给一个字符串,求区间 b o r d e r border

题解:
对于一个串 S [ l . . . r ] S[l...r] ,求的就是 m a x { i [ l , r ) l c s ( i , r ) i l + 1 } max\{i\in[l,r)|lcs(i,r)\ge i-l+1\}
这启示我们从 r r 开始跳 f a i l fail 树,到一个点的 l c a lca l e n len 就是当前的 l c s lcs
然后用线段树合并维护 R i g h t Right 集合
R i g h t Right 集合中找一个满足 i [ l , m i n ( r 1 , l e n + l 1 ) ] i\in[l,min(r-1,len+l-1)] 的最大的 i i
显然一步一步跳不行,考虑树剖,一次跳一个链,对答案有贡献的是链的一个前缀
离线,链分治,将一个询问拆成 l o g log 个,再对每一条重链单独处理
由于有贡献的是一个前缀,我们动态从上向下做
d s u   o n   t r e e dsu\ on\ tree ,从上到下做的时候暴力将轻儿子的所有点插入某一种数据结构
来看看这种数据结构需要干什么
就是在一个区间 i [ l , r ) i\in [l,r) 找一个满足 i l e n l 1 i-len\le l-1 的最大的 i i
线段树上二分,维护 i l e n i-len 的最小值
这样一来就解决了重链上一个前缀的所有轻儿子的贡献
还差一个链上的点的重儿子的贡献
对于这个重儿子的贡献,我们用最开始的线段树合并的方法就可以解决
复杂度 O ( n l o g ( n ) 2 ) O(nlog(n)^2)

一句话题解:
将问题转换为跳 f a i l fail 树,进一步转换为跳重链,链分治对每一条重链分别处理,处理的时候 d s u   o n   t r e e dsu\ on\ tree 暴力处理轻儿子,线段树合并处理重儿子
感觉链分治的思想蛮巧妙的!

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch=getchar(); if(ch=='-')f=-1; }
	while(isdigit(ch)) cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt * f; 
}
cs int N = 4e5 + 5;
char s[N]; 
int n,m,ql[N],qr[N],ans[N];
int ps[N],bin[N];
namespace SAM{
	int ch[N][26],lk[N],len[N],nd;
	void init(){ nd=n+1; for(int i=1; i<=n; i++) len[i]=i; }
	void extend(int i, int c){
		int now=i,p=(i-1)?(i-1):n+1;
		for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;
		if(!p) lk[now]=n+1;
		else{
			int q=ch[p][c];
			if(len[q]==len[p]+1) lk[now]=q;
			else{
				int cl=++nd; len[cl]=len[p]+1; lk[cl]=lk[q];
				memcpy(ch[cl],ch[q],sizeof(ch[q]));
				lk[now]=lk[q]=cl;
				for(;p&&ch[p][c]==q;p=lk[p]) ch[p][c]=cl;
			}
		}
	}
	void radix_sort(){
		for(int i=1; i<=nd; i++) ++bin[len[i]];
		for(int i=1; i<=n; i++) bin[i]+=bin[i-1];
		for(int i=nd; i>=1; i--) ps[bin[len[i]]--]=i;
	}
}
int son[N],top[N],sz[N],fa[N];
vector<int>G[N];
void Div(){
	for(int i=SAM::nd; i>=1; i--){
		int u=ps[i]; fa[u]=SAM::lk[u]; 
		sz[fa[u]]+=++sz[u];
		if(sz[son[fa[u]]]<sz[u]) son[fa[u]]=u;
		G[fa[u]].push_back(u);
	}
	for(int i=1; i<=SAM::nd; i++){
		int u=ps[i]; if(!top[u]) top[u]=u;
		if(son[u]) top[son[u]]=top[u];
	}
}
vector<int>v[N];
void addqry(int i,int u){
	if(ql[i]==qr[i]) return; 
	while(u) v[u].push_back(i),u=fa[top[u]]; 
}
namespace SGT1{
	cs int M=N*40;
	int rt[N],mx[M],ls[M],rs[M],nd;
	#define mid ((l+r)>>1)
	void ins(int &x, int l, int r, int p){
		if(!x) x=++nd; mx[x]=max(mx[x],p); if(l==r) return;
		(p<=mid)?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
	}
	int merge(int x, int y){
		if(!x||!y) return x|y;
		ls[x]=merge(ls[x],ls[y]);
		rs[x]=merge(rs[x],rs[y]);
		mx[x]=max(mx[ls[x]],mx[rs[x]]); return x;
	}
	int query(int x, int l, int r, int L, int R){
		if(!x) return 0; if(L<=l&&r<=R) return mx[x]; int ans=0;
		if(L<=mid) ans=max(ans,query(ls[x],l,mid,L,R));
		if(R>mid) ans=max(ans,query(rs[x],mid+1,r,L,R)); return ans;
	}
	void Main(){
		for(int i=1; i<=n; i++) ins(rt[i],1,n,i);
		for(int i=SAM::nd;i>=1;i--){
			int u=ps[i]; 
			for(int id:v[u])
			ans[id]=max(ans[id],query(rt[u],1,n,ql[id],min(ql[id]+SAM::len[u]-1,qr[id]-1))-ql[id]+1);
			rt[fa[u]]=merge(rt[fa[u]],rt[u]);
		}
	}
	#undef mid
}
namespace SGT2{
	cs int N=::N<<2;
	int ls[N],rs[N],mi[N],rt,nd;
	#define mid ((l+r)>>1)
	void ins(int &x, int l, int r, int p, int v){
		if(!x){ x=++nd; ls[x]=rs[x]=0; mi[x]=1e9; }
		mi[x]=min(mi[x],v); if(l==r) return; 
		(p<=mid)?ins(ls[x],l,mid,p,v):ins(rs[x],mid+1,r,p,v);
	}
	int query(int x, int l, int r, int L, int R, int v){
		if(!x||mi[x]>v) return 0; if(l==r) return l; 
		if(R<=mid) return query(ls[x],l,mid,L,R,v);
		if(L>mid) return query(rs[x],mid+1,r,L,R,v);
		int ans=0;
		if(rs[x]&&mi[rs[x]]<=v) ans=query(rs[x],mid+1,r,L,R,v);
		if(ans) return ans;
		return query(ls[x],l,mid,L,R,v);
	}
	void dsu(int u, int len){
		if(u<=n) ins(rt,1,n,u,u-len+1);
		for(int t:G[u]) dsu(t,len);
	}
	void dfs(int u){
		for(int p=u;p;p=son[p])
		for(int t:G[p]) if(t^son[p]) dfs(t);
		rt=nd=0;
		for(int p=u;p;p=son[p]){
			for(int t:G[p]) if(t^son[p]) dsu(t,SAM::len[p]);
			if(p<=n) ins(rt,1,n,p,p-SAM::len[p]+1);
			for(int id:v[p])
			ans[id]=max(ans[id],query(rt,1,n,ql[id],qr[id]-1,ql[id])-ql[id]+1);	
		}
	}
}
int main(){
	scanf("%s",s+1); n=strlen(s+1); SAM::init();
	for(int i=1; i<=n; i++) SAM::extend(i,s[i]-'a');
	SAM::radix_sort(); Div();
	m=read();
	for(int i=1; i<=m; i++){
		ql[i]=read(),qr[i]=read();
		addqry(i,qr[i]);
	}
	SGT1::Main(); 
	SGT2::dfs(n+1);
	for(int i=1; i<=m; i++) cout<<ans[i]<<'\n';
	return 0;
}
发布了634 篇原创文章 · 获赞 98 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/104086738