【BJWC2018】Border 的四種求法(SAM)(線段樹合併)(鏈分治)(DSU on Tree)

傳送門

題意:給一個字符串,求區間 borderborder

題解:
對於一個串 S[l...r]S[l...r],求的就是 max{i[l,r)lcs(i,r)il+1}max\{i\in[l,r)|lcs(i,r)\ge i-l+1\}
這啓示我們從 rr 開始跳 failfail 樹,到一個點的 lcalcalenlen 就是當前的 lcslcs
然後用線段樹合併維護 RightRight 集合
RightRight 集合中找一個滿足 i[l,min(r1,len+l1)]i\in[l,min(r-1,len+l-1)] 的最大的 ii
顯然一步一步跳不行,考慮樹剖,一次跳一個鏈,對答案有貢獻的是鏈的一個前綴
離線,鏈分治,將一個詢問拆成 loglog 個,再對每一條重鏈單獨處理
由於有貢獻的是一個前綴,我們動態從上向下做
dsu on treedsu\ on\ tree,從上到下做的時候暴力將輕兒子的所有點插入某一種數據結構
來看看這種數據結構需要幹什麼
就是在一個區間 i[l,r)i\in [l,r) 找一個滿足 ilenl1i-len\le l-1 的最大的 ii
線段樹上二分,維護 ileni-len 的最小值
這樣一來就解決了重鏈上一個前綴的所有輕兒子的貢獻
還差一個鏈上的點的重兒子的貢獻
對於這個重兒子的貢獻,我們用最開始的線段樹合併的方法就可以解決
複雜度 O(nlog(n)2)O(nlog(n)^2)

一句話題解:
將問題轉換爲跳 failfail 樹,進一步轉換爲跳重鏈,鏈分治對每一條重鏈分別處理,處理的時候 dsu on treedsu\ 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;
}
發佈了651 篇原創文章 · 獲贊 98 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章