P5546 [POI2000]公共串

P5546 [POI2000]公共串

询问多个串的最长公共子串。

利用 SAM 的 ACAM 的性质,先把每个串拿来匹配,然后沿途打标记,每次更新完一个串再更新一下对于全局的答案。

最后求出每个节点匹配的最大长度即可。

代码:

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
const int N=3e5+5,INF=1e9+7;
int x,n,last=1,tot=1,Ans[N<<1],AAns[N<<1],ans,c[N],q[N<<1];
bool flag;
char str[N];
struct SAM{
	int son[26];
	int fa,len;
}t[N<<1];
void Extend(int c){
	int p=last,np=++tot;last=np;
	t[np].len=t[p].len+1;
	while(p&&!t[p].son[c]) t[p].son[c]=np,p=t[p].fa;
	if(!p) t[np].fa=1;
	else{
		int q=t[p].son[c];
		if(t[p].len+1==t[q].len) t[np].fa=q;
		else{
			int nq=++tot;
			t[nq]=t[q];t[nq].len=t[p].len+1;
			t[np].fa=t[q].fa=nq;
			while(p&&t[p].son[c]==q) t[p].son[c]=nq,p=t[p].fa;
		}
	}
	return ;
}
int Len;
void Search(int &x,int c){
	if(t[x].son[c]) Len++,x=t[x].son[c],AAns[x]=max(AAns[x],Len);
	else{
		int p=x;
		while(p&&!t[p].son[c]) p=t[p].fa;
		if(!p) x=1,Len=0;
		else x=t[p].son[c],Len=t[p].len+1,AAns[x]=max(AAns[x],Len);
	}
	return ;
}
int main(){
	read(n);
	scanf("%s",str);int len=strlen(str);
	for(int i=0;i<len;i++) Extend(str[i]-'a');
	for(int i=1;i<=tot;i++) c[t[i].len]++;
	for(int i=1;i<=tot;i++) c[i]+=c[i-1];
	for(int i=1;i<=tot;i++) q[c[t[i].len]--]=i;
	for(int i=0;i<=tot;i++) Ans[i]=INF;
	n--;
	while(n--){
		scanf("%s",str);
		len=strlen(str);Len=0;
		for(int i=0,now=1;i<len;i++) Search(now,str[i]-'a');
		for(int i=tot;i>=1;i--){
			int x=q[i];
			AAns[t[x].fa]=max(AAns[t[x].fa],min(AAns[x],t[t[x].fa].len));
			Ans[x]=min(Ans[x],AAns[x]);AAns[x]=0;
		}
	}
	for(int i=1;i<=tot;i++) ans=max(ans,Ans[i]);
	write(ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Akmaey/p/14686725.html