POJ 3261 Milk Patterns(后缀数组)

来源:互联网 发布:淘宝买家好评 编辑:程序博客网 时间:2024/06/11 07:06

POJ 3261 Milk Patterns(后缀数组)

http://poj.org/problem?id=3261

题意:

        给你一个长N的数字串和K,要你在数字串中找到那个最长的字串,该字串在原始串中至少出现了K次.问这个最长字串的长度.

分析: 

        罗穗骞论文例题.

这道题是后缀数组的一个简单应用。做法比较简单,只需要求height数组里的最大值即可。

首先求最长重复子串,等价于求两个后缀的最长公共前缀的最大值。因为任意两个后缀的最长公共前缀都是height数组里某一段的最小值,那么这个值一定不大于height数组里的最大值。所以最长重复子串的长度就是height数组里的最大值。这个做法的时间复杂度为O(n)

        同样建立后缀数组,求出sa和height.将原始问题边为判定问题:是否存在长度为l的串至少出现了k次.

        将height分组,每组内的相邻后缀的公共前缀>=l,则看是否存在一组内的后缀数目>=k.

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=20000+100;const int maxm=1000000+100;struct SuffixArray{    int s[maxn];    int sa[maxn],rank[maxn],height[maxn];    int t1[maxn],t2[maxn],c[maxm],n;    void build_sa(int m)    {        int i,*x=t1,*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=1;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]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]? p-1:p++;            if(p>=n) break;            m=p;        }    }    void build_height()    {        int i,j,k=0;        for(i=0;i<n;i++) rank[sa[i]]=i;        for(i=0;i<n;i++)        {            if(k)k--;            j=sa[rank[i]-1];            while(s[i+k]==s[j+k]) k++;            height[rank[i]]=k;        }    }    bool check(int l,int k)//长l的出现k次以上的串    {        int cnt=0;        for(int i=1;i<n;i++)        {            if(height[i]<l)//新的一组开始                cnt=1;            else            {                cnt++;                if(cnt>=k) return true;            }        }        return false;    }}sa;int main(){    int n,k;    while(scanf("%d%d",&n,&k)==2&&n&&k)    {        for(int i=0;i<n;i++)        {            scanf("%d",&sa.s[i]);            sa.s[i]++;        }        sa.s[n]=0;        sa.n=n+1;        sa.build_sa(1000000+2);        sa.build_height();        int min=0,max=n;        while(min<max)        {            int mid=min+(max-min+1)/2;            if(sa.check(mid,k)) min = mid;            else max=mid-1;        }        printf("%d\n",min);    }    return 0;}