【BZOJ】3145: [Feyat cup 1.5]Str-set&SA

传送门:bzoj3145


题解

完全是膜着Claris的code写的,代码就不用看了。。。

不考虑复杂度我们可以枚举 i , j i,j i,j分别表示串 S , T S,T S,T中那个不同的位置。
a n s = m a x ( l c s ( i − 1 , j − 1 ) + l c p ( i + 1 , j + 1 ) ) + 1 ans=max(lcs(i-1,j-1)+lcp(i+1,j+1))+1 ans=max(lcs(i1,j1)+lcp(i+1,j+1))+1

可以在 S S S末尾加一个字符集外的字符后再连上 T T T。正着求出 l c p lcp lcp翻转后求出 l c s lcs lcs

可以通过固定一维枚举另一维 O ( n 2 ) O(n^2) O(n2)的复杂度降到 O ( n log ⁡ n ) O(n\log n) O(nlogn):从大到小枚举 l c p lcp lcp的值,即按 h e i g h t i height_i heighti排序,并查集维护 h e i g h t ≥ l c p height\geq lcp heightlcp的连续区间启发式合并 i − 1 , i i-1,i i1,i所在的区间,逐个插入更新答案。

每段连续区间中 s e t set set维护这些串对应的在 S , T S,T S,T中的 l c s lcs lcs r a n k rank rank,对于插入的串找到集合中最接近它对应的前缀 r a n k rank rank求个 l c s lcs lcs即可。

注意以上都是强制 l c s , l c p lcs,lcp lcs,lcp > 0 >0 >0的情况,还有可能只有 l c s / l c p lcs/lcp lcs/lcp,需要最后特殊枚举判断一下。


代码

#include<bits/stdc++.h>
#define mem(f) memset(f,0,sizeof(f))
using namespace std;
const int N=4e5+100;

int n,la,lb,d[18][N],bin[30],lg[N],id[N],sz[N];
int c[N],t1[N],t2[N],f[N],ans,lcp;
int head[N],nxt[N<<1],to[N<<1],tot;
char s[N],t[N];bool ca[N],cb[N];
set<int>sa[N],sb[N];
set<int>::iterator it;

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

struct SA{
	int sa[N],rk[N],ht[N];char s[N];
	inline void build()
	{
		int i,j,k,p,m=128,*x=t1,*y=t2;
		for(i=1;i<=n;++i) c[(x[i]=(int)s[i])]++;
		for(i=1;i<=m;++i) c[i]+=c[i-1];
		for(i=n;i;--i) sa[c[x[i]]--]=i;
		for(k=1;k<=n;k<<=1){
			for(p=0,i=n-k+1;i<=n;++i) y[++p]=i;
			for(i=1;i<=n;++i) if(sa[i]>k) y[++p]=sa[i]-k;
			for(i=1;i<=m;++i) c[i]=0;
			for(i=1;i<=n;++i) c[x[y[i]]]++;
			for(i=1;i<=m;++i) c[i]+=c[i-1];
			for(i=n;i;--i) sa[c[x[y[i]]]--]=y[i];
			p=1;swap(x,y);x[sa[1]]=1;
			for(i=2;i<=n;++i){
				p+=((y[sa[i]]!=y[sa[i-1]])||(y[sa[i]+k]!=y[sa[i-1]+k]));
				x[sa[i]]=p;
			}
			if(p>=n) break;
			m=p;
		}
		for(i=1;i<=n;++i) rk[sa[i]]=i;
		for(p=0,i=1;i<=n;++i){
			if(rk[i]==1) {p=0;continue;}
			for(p=max(0,p-1),j=sa[rk[i]-1];i+p<=n && j+p<=n && s[i+p]==s[j+p];++p);
			ht[rk[i]]=p;
		}
	}
	inline void mk()
	{
		int i,j;bin[0]=1;
		for(i=1;i<30;++i) bin[i]=bin[i-1]<<1;
		for(i=2;i<N;++i) lg[i]=lg[i>>1]+1;
		for(i=1;i<=n;++i) d[0][i]=ht[i];
		for(i=1;bin[i]<=n;++i)
		 for(j=1;j+bin[i]-1<=n;++j)
		  d[i][j]=min(d[i-1][j],d[i-1][j+bin[i-1]]);
	}
}A,B;

inline int cal(int x,int y)
{
	if(x>y) swap(x,y);int bs=lg[y-x];
	return min(d[bs][x+1],d[bs][y-bin[bs]+1]);
}

void dfs(int x,int fr,int bl)
{
	int i,j;f[x]=bl;
	if(A.sa[x]<=la){
		i=A.sa[x]-2;if(i>0){
			i=B.rk[n-i+1];sa[bl].insert(i);
			it=sb[bl].lower_bound(i);
			if(it!=sb[bl].end()) ans=max(ans,lcp+cal(i,*it));
			if(it!=sb[bl].begin()) it--,ans=max(ans,lcp+cal(i,*it));
		}
	}else if(A.sa[x]>la+1){
		i=A.sa[x]-2;if(i>la+1){
			i=B.rk[n-i+1];sb[bl].insert(i);
			it=sa[bl].lower_bound(i);
			if(it!=sa[bl].end()) ans=max(ans,lcp+cal(i,*it));
			if(it!=sa[bl].begin()) it--,ans=max(ans,lcp+cal(i,*it));
		}
	}
	for(i=head[x];i;i=nxt[i]) if(to[i]!=fr) dfs(to[i],x,bl);
}

inline void ext(int x,int y)
{
	x=f[x];y=f[y];
	if(sz[x]>sz[y]) swap(x,y);
	if((ca[x]&&cb[y])||(cb[x]&&ca[y])) ans=max(ans,lcp-1);
	ca[y]|=ca[x];cb[y]|=cb[x];sz[y]+=sz[x];
	sa[x].clear();sb[x].clear();
	lk(x,y);lk(y,x);dfs(x,y,y); 
}

inline bool cmp(const int&x,const int&y){return A.ht[x]>A.ht[y];}

int main(){
	int i,j;
	scanf("%s%s",s+1,t+1);
	la=strlen(s+1);lb=strlen(t+1);
	if(la==1 || lb==1) {puts("1");return 0;}
	s[(n=la+1)]='#';for(i=1;i<=lb;++i) s[++n]=t[i];
	for(i=1;i<=n;++i) A.s[i]=B.s[n-i+1]=s[i];
	B.build();B.mk();mem(c);mem(t1);mem(t2);A.build();
	for(i=1;i<=n;++i) f[i]=i,sz[i]=1;
	for(i=1;i<=n;++i){
		if(A.sa[i]<=la){
		   ca[i]=true;j=A.sa[i]-2;
		   if(j>0) sa[i].insert(B.rk[n-j+1]);
		}else if(A.sa[i]>la+1){
		   cb[i]=true;j=A.sa[i]-2;
		   if(j>la+1) sb[i].insert(B.rk[n-j+1]);
		}
	}
	for(i=1;i<n;++i) id[i]=i+1;
	sort(id+1,id+n,cmp);
	for(i=1;i<n;++i) lcp=A.ht[id[i]],ext(id[i]-1,id[i]);
	i=A.rk[2];
	for(lcp=1e9,j=i-1;j;--j){
	    lcp=min(lcp,A.ht[j+1]);
	    if(A.sa[j]>la+2) ans=max(lcp,ans);
	}
	for(lcp=1e9,j=i+1;j<=n;++j){
		lcp=min(lcp,A.ht[j]);
		if(A.sa[j]>la+2) ans=max(lcp,ans);
	}
	i=A.rk[la+3];
	for(lcp=1e9,j=i-1;j;--j){
	    lcp=min(lcp,A.ht[j+1]);
	    if(A.sa[j]>1&&A.sa[j]<=la) ans=max(lcp,ans);
	}
	for(lcp=1e9,j=i+1;j<=n;++j){
		lcp=min(lcp,A.ht[j]);
		if(A.sa[j]>1&&A.sa[j]<=la) ans=max(lcp,ans);
	}
	i=B.rk[2];
	for(lcp=1e9,j=i-1;j;--j){
		lcp=min(lcp,B.ht[j+1]);
		if(B.sa[j]>lb+2) ans=max(lcp,ans);
	}
	for(lcp=1e9,j=i+1;j<=n;++j){
		lcp=min(lcp,B.ht[j]);
		if(B.sa[j]>lb+2) ans=max(lcp,ans);
	}
	i=B.rk[lb+3];
	for(lcp=1e9,j=i-1;j;--j){
		lcp=min(lcp,B.ht[j+1]);
		if(B.sa[j]>1 && B.sa[j]<=lb) ans=max(lcp,ans);
	}
	for(lcp=1e9,j=i+1;j<=n;++j){
		lcp=min(lcp,B.ht[j]);
		if(B.sa[j]>1 && B.sa[j]<=lb) ans=max(lcp,ans);
	}
	printf("%d",ans+1);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值