洛谷 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();
}

 

posted @ 2021-08-15 10:53  TRTTG  阅读(83)  评论(0编辑  收藏  举报