bzoj 3145 [Feyat cup 1.5]Str

权限题

这题暴力就是两个串分别枚举点\(i,j\),假设这两个点不同,那么剩下的部分就是尽量越长越好,所以这部分答案就是第一个串\(i-1\)前缀和第二个串\(j-1\)前缀的\(lcs\)+第一个串\(i+1\)后缀和第二个串\(j+1\)后缀的\(lcp\)+1

考虑把两个串接起来,中间用个没用过的字符隔开,那么两个串前缀的\(lcs\)就可以先在前缀树上找到对应的两个点,然后就是两点\(lca\)\(length\).后缀的\(lcp\)也类似.那么答案就是\(\max (lcs(a_{i-1},a_{j-1})+lcp(b_{i+1},b_{j+1}))\),其中\(a_i\)为某前缀在前缀树上对应点,\(b_i\)为后缀在后缀树上对应点

所以这个问题变成了询问所有点对在两棵树上lca深度和的最大值.可以dfs第一棵树,然后处理第一棵树上lca为\(x\)的点对,每次把儿子内点在第二棵树的信息合并过来,就能求解.首先,两个子树合并,为了保证复杂度,需要启发式合并,然后在合并时算贡献.现在就是要知道某个点和一个点集内点在第二棵树上的lca最大深度,容易发现一定和dfs序相邻的点lca最深.所以一个点维护的是子树内点在第二棵树上以dfs序为关键字的set,每次二分查找前驱后继即可

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=2e5+10;
LL rd()
{
	LL x=0,w=1;char ch=0;
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
struct SAM
{
	int fa[N<<1],ch[N<<1][28],len[N<<1],la,tt;
	int ps[N],px[N<<1],sz[N<<1],de[N<<1],hs[N<<1],top[N<<1],dfn[N<<1],ti;
	vector<int> e[N<<1];
	SAM(){la=tt=1;}
	void extd(int cx,int ii)
	{
		int np=++tt,p=la;
		len[np]=len[p]+1,ps[ii]=np,px[np]=ii,la=np;
		while(!ch[p][cx]) ch[p][cx]=np,p=fa[p];
		if(!p) fa[np]=1;
		else
		{
			int q=ch[p][cx];
			if(len[q]==len[p]+1) fa[np]=q;
			else
			{
				int nq=++tt;
				fa[nq]=fa[q],len[nq]=len[p]+1;
				memcpy(ch[nq],ch[q],sizeof(ch[q]));
				fa[np]=fa[q]=nq;
				while(ch[p][cx]==q) ch[p][cx]=nq,p=fa[p];
			}
		}
	}
	void dfs1(int x)
	{
		sz[x]=1;
		vector<int>::iterator it;
		for(it=e[x].begin();it!=e[x].end();++it)
		{
			int y=*it;
			de[y]=de[x]+1,dfs1(y);
			sz[x]+=sz[y],hs[x]=sz[hs[x]]>sz[y]?hs[x]:y;
		}
	}
	void dfs2(int x,int ntp)
	{
		dfn[x]=++ti,top[x]=ntp;
		if(hs[x]) dfs2(hs[x],ntp);
		vector<int>::iterator it;
		for(it=e[x].begin();it!=e[x].end();++it)
		{
			int y=*it;
			if(y==hs[x]) continue;
			dfs2(y,y);
		}
	}
	void inii()
	{
		for(int i=2;i<=tt;++i) e[fa[i]].push_back(i);
		dfs1(1),dfs2(1,1);
	}
	int glca(int x,int y)
	{
		while(top[x]!=top[y])
		{
			if(de[top[x]]<de[top[y]]) swap(x,y);
			x=fa[top[x]];
		}
		return de[x]<de[y]?x:y;
	}
}t1,t2;
int n,m,ans;
char cc[N];
struct node
{
	int x;
	bool operator < (const node &bb) const {return t2.dfn[x]<t2.dfn[bb.x];}
};
multiset<node> s1[N<<1],s2[N<<1];
multiset<node>::iterator i1,i2,ft,nt,zr;
int id[N<<1],tot;
void dfs(int x)
{
	vector<int>::iterator it;
	id[x]=x;
	if(t1.px[x])
	{
		if(t1.px[x]+1<=n+1) s1[id[x]].insert((node){t2.ps[t1.px[x]+2]});
		else if(t1.px[x]+1>n+2&&t1.px[x]+2<=n+m+2) s2[id[x]].insert((node){t2.ps[t1.px[x]+2]});
	}
	for(it=t1.e[x].begin();it!=t1.e[x].end();++it)
	{
		int y=*it;
		dfs(y);
		if(s1[id[x]].size()+s2[id[x]].size()<s1[id[y]].size()+s2[id[y]].size()) swap(id[x],id[y]);
		bool fg1=!s1[id[x]].empty(),fg2=!s2[id[x]].empty();
		if(fg2)
		{
			for(i1=s1[id[y]].begin();i1!=s1[id[y]].end();++i1)
			{
				int xx=(*i1).x;
				nt=s2[id[x]].upper_bound(*i1);
				if(nt!=s2[id[x]].begin()) ft=--nt,++nt;
				else ft=zr;
				if((*ft).x) ans=max(ans,t1.len[x]+t2.len[t2.glca((*ft).x,xx)]+1);
				if(nt!=s2[id[x]].end()) ans=max(ans,t1.len[x]+t2.len[t2.glca((*nt).x,xx)]+1);
			}
		}
		if(fg1)
		{
			for(i2=s2[id[y]].begin();i2!=s2[id[y]].end();++i2)
			{
				int xx=(*i2).x;
				nt=s1[id[x]].upper_bound(*i2);
				if(nt!=s1[id[x]].begin()) ft=--nt,++nt;
				else ft=zr;
				if((*ft).x) ans=max(ans,t1.len[x]+t2.len[t2.glca((*ft).x,xx)]+1);
				if(nt!=s1[id[x]].end()) ans=max(ans,t1.len[x]+t2.len[t2.glca((*nt).x,xx)]+1);
			}
		}
		for(i1=s1[id[y]].begin();i1!=s1[id[y]].end();++i1) s1[id[x]].insert(*i1);
		for(i2=s2[id[y]].begin();i2!=s2[id[y]].end();++i2) s2[id[x]].insert(*i2);
	}
}

int main()
{
	cc[1]='|';
	scanf("%s",cc+2);
	n=strlen(cc+2);
	cc[n+2]='{';
	scanf("%s",cc+n+3);
	m=strlen(cc+n+3);
	for(int i=1;i<=n+m+2;++i) t1.extd(cc[i]-'a',i);
	t1.inii();
	for(int i=n+m+2;i;--i) t2.extd(cc[i]-'a',i);
	t2.inii();
	s1[0].insert((node){0});
	zr=s1[0].begin();
	dfs(1);
	printf("%d\n",ans);
	return 0; 
}
posted @ 2019-09-15 21:17  ✡smy✡  阅读(293)  评论(0编辑  收藏  举报