洛谷 P5546 [POI2000]公共串(后缀数组+并查集)
https://www.luogu.com.cn/problem/P5546
求n个单词的最长公共子串长度
单次中间用不同的字符拼起来
用后缀数组求出height之后
按height从大到小枚举lcp,并查集合并
对每个后缀标记属于哪个单词
直到合并到某个标记出现了所有单词结束
当前枚举的height就是最长公共子串长度
#include<cmath> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 10009 char ch[N]; int m,n,k,a[N],v[N],p,q,sa[2][N],rk[2][N],h[N]; int T,bl[N],fa[N],siz[N]; vector<int>hv[N]; void mul(int *sa,int *rk,int *SA,int *RK) { for(int i=1;i<=n;i++) v[rk[sa[i]]]=i; for(int i=n;i;i--) if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k; for(int i=n-k+1;i<=n;i++) SA[v[rk[i]]--]=i; for(int i=1;i<=n;i++) RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]); } void presa() { p=0; q=1; for(int i=1;i<=31;++i) v[i]=0; for(int i=1;i<=n;i++) v[a[i]]++; for(int i=1;i<=31;i++) v[i]+=v[i-1]; for(int i=1;i<=n;i++) sa[p][v[a[i]]--]=i; for(int i=1;i<=n;i++) rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i-1]]!=a[sa[p][i]]); for(k=1;k<n;k<<=1,swap(p,q)) mul(sa[p],rk[p],sa[q],rk[q]); for(int i=1,k=0;i<=n;i++) { int j=sa[p][rk[p][i]-1]; while(a[i+k]==a[j+k]) k++; h[rk[p][i]]=k;if(k) k--; } } int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); } void unionn(int x,int y) { bl[sa[p][x]]|=bl[sa[p][y]]; fa[y]=x; } void solve() { for(int i=2;i<=n;++i) hv[h[i]].push_back(i); for(int i=1;i<=n;++i) fa[i]=i; int m,f1,f2; for(int i=n-1;i>=0;--i) { m=hv[i].size(); for(int j=0;j<m;++j) { f1=find(hv[i][j]); f2=find(hv[i][j]-1); if(f1!=f2) { unionn(f1,f2); if(bl[sa[p][f1]]==(1<<T)-1) { printf("%d",i); return; } } } } } int main() { int last=0; scanf("%d",&T); for(int i=1;i<=T;++i) { scanf("%s",ch+n+1); n=strlen(ch+1); ch[++n]='a'+25+i; for(int j=last+1;j<n;++j) bl[j]=1<<i-1; last=n; } for(int i=1;i<=n;++i) a[i]=ch[i]-'a'+1; presa(); solve(); }