【poj1743】Musical Theme【后缀数组】

题目链接
题解:首先,由于可以变调,我们把字符串每个字符变成相邻两个字符的差。然后跑后缀自动机,得到sa,rnk和height。二分答案k,k表示原字符串的某个不重复公共子串的长度,就变成了判定问题。把所有排名相邻且height>=k-1的字符串分为一组,然后如果这组中sa的最大值和sa的最小值的差>=k,原串中就可以找到满足条件的2个长度为k的字符串。感性的证明:长得像的后缀都排到了一起!(捂脸)
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20005;
int m,n,a[N],c[N],t1[N],t2[N],sa[N],rnk[N],h[N];
bool cmp(int *y,int p,int q,int k){
    int a=p+k>=n?-1:y[p+k];
    int b=q+k>=n?-1:y[q+k];
    return y[p]==y[q]&&a==b;
} 
void build_sa(int m){
    int *x=t1,*y=t2;
    for(int i=0;i<m;i++){
        c[i]=0;
    }
    for(int i=0;i<n;i++){
        c[x[i]=a[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<<=1){
        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=1;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]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
        }
        if(p>=n){
            break;
        }
        m=p;
    }
    for(int i=0;i<n;i++){
        rnk[sa[i]]=i;
    }
    for(int i=0,j,k=0;i<n;i++){
        if(k){
            k--;
        }
        if(!rnk[i]){
            continue;
        }
        j=sa[rnk[i]-1];
        while(a[i+k]==a[j+k]){
            k++;
        }
        h[rnk[i]]=k;
    }
}
bool check(int k){
    int maxn=sa[0],minn=sa[0];
    for(int i=1;i<n;i++){
        if(h[i]>=k-1){
            maxn=max(maxn,sa[i]);
            minn=min(minn,sa[i]);
        }else{
            maxn=minn=sa[i];
        }
        if(maxn-minn>=k){
            return true;
        }
    }
    return false;
}
int main(){
    while(scanf("%d",&m)&&m){
        n=m-1;
        for(int i=0;i<m;i++){
            scanf("%d",&a[i]);
        }
        for(int i=0;i<n;i++){
            a[i]=a[i]-a[i+1]+88;
        }
        build_sa(200);
        int l=0,r=m/2;
        while(l<r){
            int mid=(l+r+1)/2;
            if(check(mid)){
                l=mid;
            }else{
                r=mid-1;
            }
        }
        printf("%d
",l<5?0:l);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476895.html