[poj 2274]后缀数组+最长公共子串

题目链接:http://poj.org/problem?id=2774

后缀数组真的太强大了,原本dp是O(nm)的复杂度,在这里只需要O(n+m)。

做法:将两个串中间夹一个未出现过的字符接起来,然后做一次后缀数组,得到的height相邻两个排名的后缀,在串中的位置如果满足在分界符左右两侧,就更新最长公共前缀。最后得到的最大值就是最长公共子序列。

#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;

const int MAXN = 100010*2;
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa[MAXN*3],wb[MAXN*3],wv[MAXN*3],wss[MAXN*3];
int c0(int *r,int a,int b)
{
    return r[a] == r[b] && r[a+1] == r[b+1] && r[a+2] == r[b+2];
}
int c12(int k,int *r,int a,int b)
{
    if(k == 2)
        return r[a] < r[b] || ( r[a] == r[b] && c12(1,r,a+1,b+1) );
    else return r[a] < r[b] || ( r[a] == r[b] && wv[a+1] < wv[b+1] );
}
void sort(int *r,int *a,int *b,int n,int m)
{
    int i;
    for(i = 0; i < n; i++)wv[i] = r[a[i]];
    for(i = 0; i < m; i++)wss[i] = 0;
    for(i = 0; i < n; i++)wss[wv[i]]++;
    for(i = 1; i < m; i++)wss[i] += wss[i-1];
    for(i = n-1; i >= 0; i--)
        b[--wss[wv[i]]] = a[i];
}
void dc3(int *r,int *sa,int n,int m)
{
    int i, j, *rn = r + n;
    int *san = sa + n, ta = 0, tb = (n+1)/3, tbc = 0, p;
    r[n] = r[n+1] = 0;
    for(i = 0; i < n; i++)if(i %3 != 0)wa[tbc++] = i;
    sort(r + 2, wa, wb, tbc, m);
    sort(r + 1, wb, wa, tbc, m);
    sort(r, wa, wb, tbc, m);
    for(p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)
        rn[F(wb[i])] = c0(r, wb[i-1], wb[i]) ? p - 1 : p++;
    if(p < tbc)dc3(rn,san,tbc,p);
    else for(i = 0; i < tbc; i++)san[rn[i]] = i;
    for(i = 0; i < tbc; i++) if(san[i] < tb)wb[ta++] = san[i] * 3;
    if(n % 3 == 1)wb[ta++] = n - 1;
    sort(r, wb, wa, ta, m);
    for(i = 0; i < tbc; i++)wv[wb[i] = G(san[i])] = i;
    for(i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
        sa[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];
    for(; i < ta; p++)sa[p] = wa[i++];
    for(; j < tbc; p++)sa[p] = wb[j++];
}
void da(int str[],int sa[],int rank[],int height[],int n,int m)
{
    for(int i = n; i < n*3; i++)
        str[i] = 0;
    dc3(str, sa, n+1, m);
    int i,j,k = 0;
    for(i = 0; i <= n; i++)rank[sa[i]] = i;
    for(i = 0; i < n; i++)
    {
        if(k) k--;
        j = sa[rank[i]-1];
        while(str[i+k] == str[j+k]) k++;
        height[rank[i]] = k;
    }
}

int str[MAXN*3],sa[MAXN*3],rk[MAXN],height[MAXN];

char s1[MAXN],s2[MAXN];
int l1,l2;

int main()
{
    while (~scanf("%s%s",s1,s2))
    {
        l1=strlen(s1);
        l2=strlen(s2);
        for (int i=0; i<l1; i++) str[i]=s1[i]-'a'+2;
        str[l1]=1;
        for (int i=0;i<l2;i++) str[l1+1+i]=s2[i]-'a'+2;
        str[l1+l2+1]=0;
        da(str,sa,rk,height,l1+l2+1,30);
        int ma=0;
        for (int i=2;i<=l1+l2+1;i++)
        {
            int p1=sa[i-1];
            int p2=sa[i];
            if (p1<l1&&p2>l1 || p1>l1&&p2<l1) ma=max(ma,height[i]);
        }
        printf("%d
",ma);
    }
    return 0;
}