BZOJ3796 Mushroom追妹纸 字符串 SA KMP

原文链接https://www.cnblogs.com/zhouzhendong/p/9253173.html

题目传送门 - BZOJ3796

题意

  找一个串 $w$ 满足:

  1、$w$ 是 $s_1$ 的子串

  2、$w$ 是 $s_2$ 的子串

  3、$s_3$ 不是 $w$ 的子串

  4、$w$ 的长度应尽可能大

  输出 $w$ 的长度。

  $|s_1|,|s_2|\leq 50000,|s_3|\leq 10000$

题解

  考虑求两个串 $(s_1,s_2)$ 的最长公共子串,我们可以把他们用一个特殊字符隔开,然后跑一下 $SA$ .。

  然后请你自行证明最长的一定是一个 $height[i]$ ,满足 $SA[i],SA[i-1]$ 这两个位置分别在 $s_1$ 和 $s_2$ 中。

  再考虑加上 $s_3$ 的限制。

  我们 $KMP$ 一下,看看从哪些位置开始,$s_3$ 与由 $s_1,s_2$ 构成的母串重合。

  然后预处理出对于母串的每一个位置,下一个重合点最早出现在哪里,记为 $e[i]$ ,于是从当前位置开始的串长不能超过 $e[i]-i+|s_3|-1$ ,最后统计 $height$ 的时候取个 $\min$ 即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=200005;
int m,n,l,fail[N],e[N];
int SA[N],tmp[N],rank[N],height[N],tax[N];
char s[N],t[N];
void KMP(){
	fail[0]=0,fail[1]=0;
	for (int i=2;i<=l;i++){
		int f=fail[i-1];
		while (f>0&&t[f+1]!=t[i])
			f=fail[f];
		fail[i]=f+(t[f+1]==t[i]);
	}
	int k=0;
	for (int i=1;i<=n;i++){
		while (k>0&&t[k+1]!=s[i])
			k=fail[k];
		k+=(t[k+1]==s[i]);
		if (k==l)
			k=fail[k],e[i-l+1]=i-l+1;
	}
}
void Sort(int n,int m){
	for (int i=0;i<=m;i++)
		tax[i]=0;
	for (int i=1;i<=n;i++)
		tax[rank[i]]++;
	for (int i=1;i<=m;i++)
		tax[i]+=tax[i-1];
	for (int i=n;i>=1;i--)
		SA[tax[rank[tmp[i]]]--]=tmp[i];
}
bool cmp(int rk[],int x,int y,int w){
	return rk[x]==rk[y]&&rk[x+w]==rk[y+w];
}
void Suffix_Array(char s[],int n){
	memset(SA,0,sizeof SA);
	memset(tmp,0,sizeof tmp);
	memset(rank,0,sizeof rank);
	memset(height,0,sizeof height);
	int m=200;
	for (int i=1;i<=n;i++)
		rank[i]=s[i],tmp[i]=i;
	Sort(n,m);
	for (int w=1,p=0;p<n;w<<=1,m=p){
		p=0;
		for (int i=n-w+1;i<=n;i++)
			tmp[++p]=i;
		for (int i=1;i<=n;i++)
			if (SA[i]>w)
				tmp[++p]=SA[i]-w;
		Sort(n,m);
		swap(tmp,rank);
		rank[SA[1]]=p=1;
		for (int i=2;i<=n;i++)
			rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;
	}
	for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)
		for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);
	height[1]=0;
}
bool Type(int x){
	return x>m;
}
int main(){
	scanf("%s",s+1);
	m=strlen(s+1)+1;
	s[m]='*';
	scanf("%s",s+m+1);
	n=strlen(s+1);
	scanf("%s",t+1);
	l=strlen(t+1);
	for (int i=1;i<=n;i++)
		e[i]=n+1;
	KMP();
	for (int i=n-1;i>=1;i--)
		e[i]=min(e[i],e[i+1]);
	Suffix_Array(s,n);
	int ans=0;
	for (int i=2;i<=n;i++)
		if (Type(SA[i])^Type(SA[i-1]))
			ans=max(ans,min(height[i],e[SA[i]]-SA[i]+l-1));
	printf("%d",ans);
	return 0;
}

  

posted @ 2018-07-02 11:54  zzd233  阅读(235)  评论(0编辑  收藏  举报