POJ2774 - Long Long Message

Portal

Description

求两个字符串\(s_1,s_2(|s_1|,|s_2|\leq10^5)\)的最长公共子串。

Solution

复习一波后缀数组。
将两个串连接成s1+'#'+s2,求后缀数组。若\(sa[i]\)\(sa[i-1]\)不在同一串中,用\(h[i]\)更新答案。

时间复杂度\(O(|s|log|s|)\)

Code

//Long Long Message
#include <cstdio>
#include <cstring>
int max(int x,int y) {return x>y?x:y;}
int const N=2e5+10;
int n; char s[N];
int sa[N],rank[N<<1],h[N];
int cnt[N],tmp[N],rank1[N];
int main()
{
    scanf("%s",s+1); n=strlen(s+1);
    int L1=n; s[++n]='#';
    scanf("%s",s+n+1); n=strlen(s+1);
    for(int i=1;i<=n;i++) cnt[s[i]]=1;
    for(int i=1;i<=256;i++) cnt[i]+=cnt[i-1];
    for(int i=1;i<=n;i++) rank[i]=cnt[s[i]];
    for(int L=1;L<=n;L<<=1)
    {
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;i++) cnt[rank[i+L]]++;
        for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
        for(int i=n;i>=1;i--) tmp[cnt[rank[i+L]]--]=i;
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;i++) cnt[rank[tmp[i]]]++;
        for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
        for(int i=n;i>=1;i--) sa[cnt[rank[tmp[i]]]--]=tmp[i];
        int k=0;
        for(int i=1;i<=n;i++)
        {
            if(rank[sa[i]]!=rank[sa[i-1]]||rank[sa[i]+L]!=rank[sa[i-1]+L]) k++;
            rank1[sa[i]]=k;
        }
        memcpy(rank,rank1,sizeof rank1);
        if(k>=n) break;
    }
    for(int i=1,k=0;i<=n;i++)
    {
        if(rank[i]==1) {h[1]=k=0; continue;}
        if(k) k--;
        while(s[i+k]==s[sa[rank[i]-1]+k]) k++;
        h[rank[i]]=k;
    }
    int ans=0;
    for(int i=1;i<=n;i++) if((sa[i]>L1)^(sa[i-1]>L1)) ans=max(ans,h[i]);
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-03-22 14:30  VisJiao  阅读(150)  评论(0编辑  收藏  举报