题目描述:给一组数字串,求不可重叠的最长公共子串,如果长度大于5,输出长度,否则输出0。

注意:“公共子串”的定义为,两子串的对应位置的元素的差值相同即可。

如:1 2 3 和 6 7 8 即满足条件,他们对应位置的差值是2。


题解:我们如果有两个满足条件的子序列a[i],a[i+1],…a[i+k]和b[i],b[i+1],…b[i+k],则有:b[i]-a[i]=b[i+1]-a[i+1]= …b[i+k]-a[i+k]。

我们将原数组的元素依次两两做差,差值加上88,形成一个新数组x[],最后补一个0,使x数组有n个元素。

若有b[i+1]-b[i]=a[i+1]-a[i],则有x[j]=x[k](j和k分别对应b[i+1]和a[i+1]在原数组中的位置)。移项后,b[i+1]-a[i+1]=b[i]-a[i]。

若有b[i+2]-b[i+1]=a[i+2]-a[i+1],则有x[j+1]=x[k+1]。移项后,b[i+2]-a[i+2]=b[i+1]-a[i+1]。

若有b[i+3]-b[i+2]=a[i+3]-a[i+2],则有x[j+2]=x[k+2]。移项后,b[i+3]-a[i+3]=b[i+2]-a[i+2]。

......

发现若x数组中有长度为m的公共子串,则说明原数组中有长度为m+1的符合条件的子串。

接下来用后缀数组+二分解决即可。

先二分答案,把题目变成判定性问题:判断是否存在两个长度为 k 的子串是相同的,且不重叠。解决这个问题的关键还是利用height 数组。把排序后的后缀分成若干组,其中每组的后缀之间的 height 值都不小于 k。例如,字符串为“aabaaaab”,当 k=2 时,后缀分成了 4 组


容易看出,有希望成为最长公共前缀不小于 k 的两个后缀一定在同一组。然后对于每组后缀,只须判断每个后缀的 sa 值的最大值和最小值之差是否不小于k。如果有一组满足,则说明存在,否则不存在。


代码:

#include<cstdio>
#include<algorithm>
#define N 20010
#define LL long long
using namespace std;

int s[N];
int sa[N],t[N],t2[N],c[N],n,rak[N],height[N];

void build_sa(int m,int *s)
{
    int i,*x=t,*y=t2;
    for (i=0;i<m;i++)c[i]=0;
    for (i=0;i<n;i++)c[x[i]=s[i]]++;
    for (i=1;i<m;i++)c[i]+=c[i-1];
    for (i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for (int k=1;k<=n;k<<=1)
    {
        int p=0;
        for (i=n-k;i<n;i++)y[p++]=i;
        for (i=0;i<n;i++)if (sa[i]>=k)y[p++]=sa[i]-k;
        for (i=0;i<m;i++)c[i]=0;
        for (i=0;i<n;i++)c[x[y[i]]]++;
        for (i=0;i<m;i++)c[i]+=c[i-1];
        for (i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1; x[sa[0]]=0;
        for (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;
    }
}

void getheight()
{
    int i,j,k=0;
    for (i=0;i<n;i++)rak[sa[i]]=i;
    for (i=0;i<n;i++)
    {
        if (k)k--;
        if (!rak[i])continue;
        j=sa[rak[i]-1];
        while (s[i+k]==s[j+k])k++;
        height[rak[i]]=k;
    }
}

bool check(int k)
{
    int x,y;
    for (int i=1;i<n;i++)
    {
        if (height[i]<k)
        {
            x=0;y=1000000;
        }else
        {
            x=max(x,sa[i]);
            y=min(y,sa[i]);
            x=max(x,sa[i-1]);
            y=min(y,sa[i-1]);
            if (x-y>=k)return true;
        }
    }
    return false;
}

int main()
{

    while (~scanf("%d",&n) && n)
    {
        for (int i=0;i<n;i++) scanf("%d",&s[i]);
        for (int i=0;i<n-1;i++) s[i]=s[i+1]-s[i]+88; s[n-1]=0;
        build_sa(200,s);
        getheight();
        int l=0,r=n;
        while (r-l>1)
        {
            int t=(l+r)>>1;
            bool f=check(t);
            if (f) l=t;else r=t;
        }
        int ans;
        if (check(r)) ans=r;else ans=l;
        if (ans>=4)printf("%d\n",ans+1);else puts("0");
    }
    return 0;
}