bzoj3796 mushroom追妹子

http://www.elijahqi.win/archives/630
Description
Mushroom最近看上了一個漂亮妹紙。他選擇一種非常經典的手段來表達自己的心意——寫情書。考慮到自己的表達能力,Mushroom決定不手寫情書。他從網上找到了兩篇極佳的情書,打算選擇其中共同的部分。另外,Mushroom還有個一個情敵Ertanis,此人也寫了封情書給妹子。
Mushroom不希望自己的情書中完整的出現了情敵的情書。(這樣抄襲的事情就暴露了)。
Mushroom把兩封情書分別用字符串s1和s2來表示,Ertanis的情書用字符串s3來表示,他要截取的部分用字符串w表示。
需滿足:
1、w是s1的子串
2、w是s2的子串
3、s3不是w的子串
4、w的長度應儘可能大
所謂子串是指:在字符串中連續的一段
【輸入】
輸入文件爲girl.in
輸入有三行,第一行爲一個字符串s1第二行爲一個字符串s2,
第三行爲一個字符串s3。輸入僅含小寫字母,字符中間不含空格。
【輸出】
輸出文件爲girl.out
輸出僅有一行,爲w的最大可能長度,如w不存在,則輸出0。
【輸入樣例】
abcdef
abcf
bc
【輸出樣例】
2
【樣例解釋】
s1和s2的公共子串有abc,ab,bc,a,b,c,f,其中abc,bc包含子串bc不合法,所以最長的合法子串爲ab。
【數據規模】
對於30%的數據:1<=s1、s2、s3的長度<=500
對於60%的數據:1<=s1、s2、s3的長度<=5000
對於100%的數據:1<=s1、s2的長度<=50000,1<=s3的長度<=10000

Input
輸入有三行,第一行爲一個字符串s1第二行爲一個字符串s2,
第三行爲一個字符串s3。輸入僅含小寫字母,字符中間不含空格。

Output
輸出僅有一行,爲w的最大可能長度,如w不存在,則輸出0。

Sample Input
abcdef

abcf

bc

Sample Output
2

【樣例解釋】

s1和s2的公共子串有abc,ab,bc,a,b,c,f,其中abc,bc包含子串bc不合法,所以最長的合法子串爲ab。

HINT

對於100%的數據:1<=s1、s2的長度<=50000,1<=s3的長度<=10000

題意要求我們找一個串1&串2完整包含的子串,並且串3不完整在他們裏面

首先我們需要製作一個L數組表示L[i] 表示 從L[i]->i這個區間完整包含了s3並且這個區間的長度最小

如何製作L數組我們可以用kmp去匹配

然後二分驗證答案是否正確 中間有分隔符,不要忘記處理

#include<cstdio>
#include<cstring>
#define N 110000
int n;char s[N>>1],t[N>>1];int a[N],len[5],rank[N<<1],rank1[N],height[N],tmp[N],sa[N],count[N],L[N],bl[N],next[N>>1];
inline int max(int a,int b){
	return a>b?a:b;
}
inline bool judge(int x){
	bool f[3];f[1]=f[2]=false;
	for (int i=1;i<=n;++i){
		if (height[i]>=x){
			if(sa[i]<=L[sa[i]+x-1]) continue;
			f[bl[sa[i]]]=true;
		}else{
			if (f[1]&&f[2]) return true;
			f[1]=f[2]=false;
			if (sa[i]<=L[sa[i]+x-1])continue;
			f[bl[sa[i]]]=true;
		}
	}
	return false;
}
int main(){
	//freopen("bzoj3796.in","r",stdin);
	n=1;int l=1,r=0;int m=30;
	for (int i=1;i<=2;++i){
		scanf("%s",s+1);len[i]=strlen(s+1);
		r=max(r,len[i]);for (int j=0;j<len[i];++j) a[j+n]=s[j+1]-'a'+1,bl[j+n]=i;
		n+=len[i];a[n++]=m++;
	}n-=1;
	scanf("%s",t+1);len[3]=strlen(t+1);
	//sa
	for (int i=1;i<=n;++i) count[a[i]]=1;
	for (int i=1;i<=m;++i) count[i]+=count[i-1];
	for (int i=1;i<=n;++i) rank[i]=count[a[i]];
	int k=0;
	for (int p=1;k!=n;p<<=1,m=k){
		for (int i=1;i<=m;++i) count[i]=0;
		for (int i=1;i<=n;++i) count[rank[i+p]]++;
		for (int i=1;i<=m;++i) count[i]+=count[i-1];
		for (int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
		for (int i=1;i<=m;++i) count[i]=0;
		for (int i=1;i<=n;++i) count[rank[i]]++;
		for (int i=1;i<=m;++i) count[i]+=count[i-1];
		for (int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
		memcpy(rank1,rank,sizeof(rank)>>1);
		rank[sa[1]]=k=1;
		for (int i=2;i<=n;++i){
			if (rank1[sa[i]+p]!=rank1[sa[i-1]+p]||rank1[sa[i]]!=rank1[sa[i-1]])++k;
			rank[sa[i]]=k;
		}
	}
	k=0;
	for (int i=1;i<=n;++i){
		if (rank[i]==1) continue;
		k=k==0?0:k-1;
		while (a[i+k]==a[sa[rank[i]-1]+k])++k;
		height[rank[i]]=k;
	}
	//getnext
	next[1]=0;
	for (int i=2;i<=len[3];++i){
		while (k&&t[k+1]!=t[i]) k=next[k];
		if (t[k+1]==t[i])++k;
		next[i]=k;
	}
	// make Array L[]
	int last=0;k=0;
	for (int i=1;i<=len[2];++i){
		while (k&&s[i]!=t[k+1]) k=next[k];
		if (s[i]==t[k+1]) ++k;
		if (k==len[3]) last=len[1]+1+i-len[3]+1;//別忘記處理分隔符x
		L[len[1]+i+1]=last; 
	}
	while (l<=r){
		int mid=(l+r)>>1;
		if (judge(mid)) l=mid+1;else r=mid-1;
	}
	printf("%d",l-1);
	return 0;
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章