您的位置:首页 > 其它

POJ 3261 后缀数组:求可重叠的最长重复子串

2014-08-13 12:08 344 查看
后缀思想及解法的来源:http://wenku.baidu.com/link?url=kG_SolxPKmyHjwKCvnQ5bxVMUAA3FU-3HFSBASW_jLSgku9u9r9PKHKGqf4CF9RTxG4gfkYg6lMCdGZu2eVMCvidEtcObiAXLP_9-3PhKp3

思路:这题的做法和POJ 1743差不多,也是先二分答案,然后将后缀分成若干组。不同的是,这里要判断的是有没有一个组的后缀个数不小于k。如果有,那么存在k个相同的子串满足条件,否则不存在。这个做法的时间复杂度为O(nlogn)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<bitset>
#define mem(a,b) memset(a,b,sizeof(a))
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define llson j<<1,l,mid
#define rrson j<<1|1,mid+1,r
#define INF 0x7fffffff
#define maxn 20010
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
void radix(int *str,int *a,int *b,int n,int m)
{
    static int count[maxn*2];
    mem(count,0);
    for(int i=0;i<n;i++) ++count[str[a[i]]];
    for(int i=1;i<=m;i++) count[i]+=count[i-1];
    for(int i=n-1;i>=0;i--) b[--count[str[a[i]]]]=a[i];
}
void suffix(int *str,int *sa,int n,int m) //倍增算法计算出后缀数组sa
{
    static int rank[maxn*2],a[maxn*2],b[maxn*2];
    for(int i=0;i<n;i++) rank[i]=i;
    radix(str,rank,sa,n,m);
    rank[sa[0]]=0;
    for(int i=1;i<n;i++)
        rank[sa[i]]=rank[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);
    for(int i=0;1<<i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            a[j]=rank[j]+1;
            b[j]=j+(1<<i)>=n?0:rank[j+(1<<i)]+1;
            sa[j]=j;
        }
        radix(b,sa,rank,n,n);
        radix(a,rank,sa,n,n);
        rank[sa[0]]=0;
        for(int j=1;j<n;j++)
            rank[sa[j]]=rank[sa[j-1]]+(a[sa[j-1]]!=a[sa[j]]||b[sa[j-1]]!=b[sa[j]]);
    }
}
void calcHeight(int *str,int *sa,int *h,int n) //求出最长公共前缀数组h
{
    static int rank[maxn*2];
    int k=0;
    h[0]=0;
    for(int i=0;i<n;i++) rank[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        k=k==0?0:k-1;
        if(rank[i])
            while(str[i+k]==str[sa[rank[i]-1]+k]) k++;
        else k=0;
        h[rank[i]]=k;
    }
}
int a[maxn],sa[maxn],height[maxn];
bool binary(int mid,int n,int k)
{
    int i=1;
    while(1)
    {
        while(i<n&&height[i]<mid) i++;
        if(i==n) break;
        int cnt=1; //>=mid分在一组
        while(i<n&&height[i]>=mid) cnt++,i++;
        if(cnt>=k) return true;
    }
    return false;
}
int main()
{
    //freopen("test.txt","r",stdin);
    int n,k,i;
    while(~scanf("%d%d",&n,&k))
    {
        for(i=0;i<n;i++)
            scanf("%d",a+i);
        suffix(a,sa,n,20000);
        calcHeight(a,sa,height,n);
        //for(i=1;i<=n;i++)
        //    cout<<height[i]<<' '<<sa[i]<<endl;
        int l=1,r=n,mid,ans=0;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(binary(mid,n,k)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐