POJ-1743-Musical Theme(后缀数组)

题意:

找到一个字符串内长度不小于五的可不重叠的最长重复子串,子串的所有数字同时加上或减去一同一个数这时两个字串重复也算重复出现。

分析:

首先最所有数字作差,s[i]=s[i+1]-s[i],这样一来及时原先的字串是进行过加或减那他们的差值也会相同,所以转换为求现有s[]中大与5的最长重复字串,用后缀数组。

首先由二分答案的方法将问题变成判定性的:长度大于k的重复字串有没有?然后将height数组分组,每组内的后缀之间的height都要大于k,如果每组内的后缀之间的最长公共前缀有大于k的而且这两个后缀的sa[]之差大于k就说明存在长度至少为k的不重复子串。

求最长公共前缀就要用到height数组,因为这组中任意两个后缀的公共前缀必定是某些height值中的最小值,而这个值如果最大则一定是这组中height中的最大值。

 
// File Name: 1743.cpp
// Author: Zlbing
// Created Time: 2013年09月04日 星期三 20时27分47秒

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
using namespace std;
#define CL(x,v); memset(x,v,sizeof(x));
#define INF 0x3f3f3f3f
#define LL long long
#define REP(i,r,n) for(int i=r;i<=n;i++)
#define RREP(i,n,r) for(int i=n;i>=r;i--)
//rank从0开始
//sa从1开始,因为最后一个字符(最小的)排在第0位
//height从2开始,因为表示的是sa[i-1]和sa[i]
const int MAXN=20050;
int rank[MAXN],sa[MAXN],X[MAXN],Y[MAXN],height[MAXN],s[MAXN];
int buc[MAXN];
int n;
void calheight(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] ; s[i+k] == s[j+k] ; k++);
}
bool cmp(int *r,int a,int b,int l) {
    return (r[a] == r[b] && r[a+l] == r[b+l]);
}
void suffix(int n,int m = 128) {
    int i , l , p , *x = X , *y = Y;
    for(i = 0 ; i < m ; i ++) buc[i] = 0;
    for(i = 0 ; i < n ; i ++) buc[ x[i] = s[i]  ] ++;
    for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
    for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
    for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
        p = 0;
        for(i = n-l ; i < n ; i ++) y[p++] = i;
        for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
        for(i = 0 ; i < m ; i ++) buc[i] = 0;
        for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
        for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
        for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
        for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
            x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
    }
    calheight(n-1);//后缀数组关键是求出height,所以求sa的时候顺便把rank和height求出来
}
bool judge(int x)
{
    int l=sa[1],r=sa[1];
    for(int i=2;i<n;i++)
    {
        if(height[i]<x)
        {
            l=sa[i];
            r=sa[i];
            continue;
        }
        l=min(l,sa[i]);
        r=max(r,sa[i]);
        if(r-l>=x)return true;
    }
    return false;
}
int main() {

    while(~scanf("%d",&n))
    {
        if(!n)break;
        REP(i,0,n-1)
            scanf("%d",&s[i]);
        REP(i,0,n-2)
            s[i]=s[i+1]-s[i]+90;
        s[n-1]=0;
        suffix(n,200);
        int l=0,r=n,mid;
        int ans=-1;
        while(l<=r)
        {
            mid=l+(r-l+1)/2;
            if(judge(mid))
            {
                ans=max(mid,ans);
                l=mid+1;
            }
            else r=mid-1;
        }
        if(ans>=4)
        printf("%d\n",ans+1);
        else printf("0\n");
    }
    return 0;
}

 

posted @ 2013-09-05 21:13  z.arbitrary  阅读(245)  评论(0编辑  收藏  举报