●BZOJ 3796 Mushroom追妹纸

Posted *ZJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了●BZOJ 3796 Mushroom追妹纸相关的知识,希望对你有一定的参考价值。

题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=3796

题解:


题意:
    给出三个串 A,B,C
    找出一个最长串 S,
    使得 S是A,B 的子串,但是 C不是S的子串。
 



首先,对于第一二个限制,只需要把 A,B串用一个分隔符连接在一起。
求出sa[],rank[],height[]数组,
那么在排好序的后缀中,相邻的两个后缀如果一个属于A串(令在A串p位置),另一个属于B串的话,
则可能贡献答案。 但应该贡献多少呢?
由于有 C 的限制,即在LCP中选出的最长前缀里不能出现 C串。
所以考虑如下做法。
定义 appear[i] 表示 A串的 i位置后面第一次出现 C串的开始位置。
求出这个以后,那么就可以贡献答案了:
ANS=max(ANS,min(hei[i],appear[i]-p+LenC-1))
以下是求pre数组的做法:
把 A串作为文本串,C串作为匹配串,用 KMP去匹配。
并在 A串中每一个出现 C串的开始位置打上标记
然后反向跑一边,得出appear[]数组。

总的复杂度 (Nlog2N+N)

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 100500
#define INF 0x3f3f3f3f
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
char S[MAXN],A[MAXN],B[MAXN],C[MAXN];
int sa[MAXN],rak[MAXN],hei[MAXN],app[MAXN];
int L1,L2,L3,ANS;
void build(int N,int M){
	static int cc[MAXN],ta[MAXN],tb[MAXN],*x,*y,h,p;
	x=ta; y=tb; h=0;
	for(int i=0;i<M;i++) cc[i]=0;
	for(int i=0;i<N;i++) cc[x[i]=S[i]]++;
	for(int i=1;i<M;i++) cc[i]+=cc[i-1];
	for(int i=N-1;i>=0;i--) sa[--cc[x[i]]]=i;
	for(int k=1;p=0,k<N;k<<=1){
		for(int i=N-k;i<N;i++) y[p++]=i;
		for(int i=0;i<N;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
		for(int i=0;i<M;i++) cc[i]=0;
		for(int i=0;i<N;i++) cc[x[y[i]]]++;
		for(int i=1;i<M;i++) cc[i]+=cc[i-1];
		for(int i=N-1;i>=0;i--) sa[--cc[x[y[i]]]]=y[i];
		swap(x,y); y[N]=-1; x[sa[0]]=0; M=1;
		for(int i=1;i<N;i++)
			x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?M-1:M++;
		if(M>=N) break;
	}
	for(int i=0;i<N;i++) rak[sa[i]]=i;
	for(int i=0,j;i<N;i++){
		if(h) h--;
		if(rak[i]){
			j=sa[rak[i]-1];
			while(S[i+h]==S[j+h]) h++;
		}
		hei[rak[i]]=h;
	}
}
void KMP(char *T,char *S){
	static int nxt[MAXN],i,j,k,lT,lS;
	memset(app,0x3f,sizeof(app));
	nxt[0]=-1; lT=strlen(T); lS=strlen(S);
	j=0; k=-1;
	while(j<lS){
		if(k==-1||S[j]==S[k]) j++,k++,nxt[j]=k;
		else k=nxt[k];
	}
	i=0; j=0;
	while(i<lT){
		if(j==-1||T[i]==S[j]){
			i++; j++;
			if(!S[j]) app[i-lS]=i-lS;
		}
		else j=nxt[j];
	}
	for(int i=lT-1;i>=0;i--) app[i]=min(app[i],app[i+1]);
}
bool bel(int p){
	if(p<L1) return 0;
	else return 1;
}
int main()
{
	int N;
	scanf(" %s %s %s",A,B,C);
	L1=strlen(A); L2=strlen(B); L3=strlen(C); N=L1+L2+1;
	for(int i=0;i<L1;i++) S[i]=A[i]; S[L1]=‘&‘;
	for(int i=0;i<L2;i++) S[L1+i+1]=B[i]; S[N]=0;
	build(N,300);
	KMP(A,C);
	for(int i=1,p,q;i<N;i++){
		if(hei[i]<=ANS||!(bel(sa[i-1])^bel(sa[i]))) continue;
		if(!bel(sa[i-1])) p=sa[i-1]; else p=sa[i]; q=app[p];
		ANS=max(ANS,min(hei[i],q-p+L3-1));
	}
	printf("%d",ANS);
	return 0;
}

以上是关于●BZOJ 3796 Mushroom追妹纸的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 3796 Mushroom追妹纸——后缀数组

bzoj 3796 Mushroom追妹纸 —— 后缀数组

2018.7.31 Noip2018模拟测试赛(十六)

bzoj3796

新来的长腿程序猿妹纸

(最短路)17bupt新生赛——F. ch追妹