后缀数组的使用心得——POJ2774 最长连续公共子串

对于这道题,将两个字符串直接合并成为一个字符串,分别记录连个字符串结束的位置。

首先,应用黑暗圣典的模板,我们可以顺利得到height,rank,sa三个数组。

之后直接扫描1-n所有的位置,选出来一个,符合“两者都在不同子串的最大长度即可”。

此时我们会发现,sa数组记录了每个子串开头的位置,可以用于判断。

 

#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;

const long long MAXN=200233;
const long long INF=1e8+233;
char s[MAXN];
int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],n;
int rankk[MAXN],height[MAXN];
void getHeight()
{
    int k=0;
    for(int i=0;i<n;++i)rankk[sa[i]]=i;
    for(int i=0;i<n;++i)
    {
        if(k)k--;
        int j=sa[rankk[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rankk[i]]=k;
    }
} 


void build_sa(int m)
{
    int i,*x=t,*y=t2;
    
    for(int i=0;i<m;++i)c[i]=0;
    for(int i=0;i<n;++i)c[x[i]=s[i]]++;
    for(int i=1;i<m;++i)c[i]+=c[i-1];
    for(int i=n-1;i>=0;--i)sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k*=2)
    {
        int p=0;
        for(int i=n-k;i<n;++i)y[p++]=i;
        for(int i=0;i<n;++i)if(sa[i]>=k)y[p++]=sa[i]-k;
        
        for(int i=0;i<m;++i)c[i]=0;
        for(int i=0;i<n;++i)c[x[y[i]]]++;
        for(int i=0;i<m;++i)c[i]+=c[i-1];
        for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(int i=1;i<n;++i)
        {
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
             
        }
        if(p>=n)break;
        m=p;
    }
}

 


int main()
{
//    cin.sync_with_stdio(false);
    scanf("%s",s);
    int len=strlen(s);
    scanf("%s",s+len);
    int len2=strlen(s);
    n=len2+1;
    build_sa(233);
    getHeight();
    int maxx=-1;
    for(int i=1;i<n;++i)
    {
//        cout<<height[i]<<ends<<s+sa[i]<<endl;    
        if((sa[i]>=len&&sa[i-1]<len)||(sa[i]<len&&sa[i-1]>=len))maxx=max(maxx,height[i]);
    }
    cout<<maxx<<"\n";
//    cout<<s<<endl;
//    cout<<len<<ends<<len2<<endl;
//    
    return 0;
} 

 

posted @ 2017-09-12 11:27  六花的邪王真眼  阅读(166)  评论(0编辑  收藏  举报