POJ 1743 后缀数组

题意:一道论文题,给定一串数组,求最长的重复子串长度,其中这两个子串不重叠,这两个子串可以相差同一个数字。

分析:后一个减前一个数字,那么对于上面说的,可以相差同一个数字就变成了相同数字。问题就变成了,求一个串里面的最长不可重复的子串长度。

但是不再是height数组里面的最大值了,二分答案,将height分成若干组(height数组是已经根据sa数组排好了的),记录每组里面的sa[] 最小值,sa[] 最大值,L + mid + 1<=R 即不重叠。

最后大佬模板就是硬!

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1000000+5;

int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int sa[maxn];
int r[maxn];

int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++) ws[i]=0;
    for(i=0; i<n; i++) ws[x[i]=r[i]]++;
    for(i=1; i<m; i++) ws[i]+=ws[i-1];
    for(i=n-1; i>=0; i--) sa[--ws[x[i]]]=i;
    for(j=1,p=1; p<n; j*=2,m=p)
    {
        for(p=0,i=n-j; i<n; i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<n; i++) wv[i]=x[y[i]];
        for(i=0; i<m; i++) ws[i]=0;
        for(i=0; i<n; i++) ws[wv[i]]++;
        for(i=1; i<m; i++) ws[i]+=ws[i-1];
        for(i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return;
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
    int i,j,k=0;
    for(i=1; i<=n; i++) rank[sa[i]]=i;
    for(i=0; i<n; height[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);
    return;
}



int a[maxn];
int n;

const int INF = 0x3f3f3f3f;


int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&n),n)
    {
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);

        for(int i=0; i<n-1; i++)
        {
            r[i] = a[i+1] - a[i]+100;
        }

        n--;

        da(r,sa,n+1,300);
        calheight(r,sa,n);

        int l = 1,r=n+1,mid;
        int L,R;
        while(l<=r)
        {
            mid = (l+r)/2;
            L = INF,R = -INF;
            bool flag = false;
            for(int i=1; i<=n; i++)
            {
                if(height[i]>=mid)
                {
                    L = min(L,sa[i]);
                    L = min(L,sa[i-1]);
                    R = max(R,sa[i]);
                    R = max(R,sa[i-1]);
                }
                else
                {
                    if(L+mid+1<=R)
                    {
                        flag = true;
                        //break;
                    }
                    L = INF;
                    R = -INF;
                }
            }
            if(L+mid+1<=R) flag = true;
            if(flag) l = mid + 1;
            else r = mid - 1;
        }

        if(l>=5)
            printf("%d
",l);
        else puts("0");
    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/TreeDream/p/7221484.html