[POJ3261 Milk Patterns]

[关键字]:后缀数组 字符串

[题目大意]:给定一个字符串,求至少出现k 次的最长重复子串,这k 个子串可以重叠。

//==========================================================================================

[分析]:先二分答案判断有没有至少出现k 次的长度为x的重复子串。因为可以重复所以只要heighe数组中有>x说明有长度为x的可重复子串,然后再根据height分组:如果height[i]<x就把i和上一组分开,这样每一组里都存在长为x的重复字串,如果有任意一组里有超过k个,说明成立。

[代码]:

View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN=21000;

int n,k;
int s[MAXN];
int top[MAXN],height[MAXN],rank[MAXN],sa[MAXN],temp[MAXN];

void Make()
{
memset(top,0,sizeof(top));
int i,j,len,m;
m=n<256?256:n;
for (i=0;i<n;++i) ++top[rank[i]=s[i]&0xff];
for (i=1;i<m;++i) top[i]+=top[i-1];
for (i=0;i<n;++i) sa[--top[rank[i]]]=i;
for (len=1;len<n;len<<=1)
{
for (i=0;i<n;++i)
{
j=sa[i]-len;
if (j<0) j+=n;
temp[top[rank[j]]++]=j;
}
sa[temp[top[0]=0]]=j=0;
for (i=1;i<n;++i)
{
if (rank[temp[i]]!=rank[temp[i-1]] ||
rank[temp[i]+len]!=rank[temp[i-1]+len])
top[++j]=i;
sa[temp[i]]=j;
}
memcpy(rank,sa,sizeof(sa));
memcpy(sa,temp,sizeof(sa));
if (j>n-1) break;
}
}

void Lcp()
{
int i,j,k;
for (j=rank[height[k=i=0]=0];i<n-1;++i,++k)
while (k>=0 && s[i]!=s[sa[j-1]+k])
height[j]=k--,j=rank[sa[j]+1];
}

bool Judge(int x)
{
int sum=1;
for (int i=1;i<n;++i)
{
if (height[i]<x) sum=1; else
{
++sum;
if (sum>=k) return 1;
}
}
return 0;
}

void Solve()
{
int l=1,r=n,mid,ans;
while (l<=r)
{
mid=(l+r)/2;
//printf("%d %d %d\n",l,r,mid);
if (Judge(mid)) ans=mid,l=mid+1; else r=mid-1;
}
printf("%d\n",ans);
}

int main()
{
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d%d",&n,&k);
for (int i=0;i<n;++i) scanf("%d",&s[i]),++s[i];
s[n++]=0;
Make();
Lcp();
Solve();
return 0;
}



你可能感兴趣的:(Pattern)