[POJ1743 Musical Theme](USACO5.1.3)

[关键字]:后缀数组

[题目大意]:找到一个字符串内长度不小于五的最长不重复字串,字串的所有数字同时加上或减去一同一个数也算重复出现。这时usaco5.1.3的原题但数据范围变大了。

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

[分析]:首先最所有数字作差,原数据为f[i]新数据为s[i]=f[i-1]+f[i],这样一来及时原先的字串是进行过加或减那他们的差值也会相同,所以转换为求现有s[]中大与5的最长重复字串,用后缀数组。首先由二分答案的方法将问题变成判定性的:长度大于k的重复字串有没有?然后将height数组分组,每组内的后缀之间的height都要大于k,如果每组内的后缀之间的最长公共前缀有大于k的而且这两个后缀的sa[]之差大于k就说明存在长度至少为k的不重复子串。求最长公共前缀就要用到height数组,因为这组中任意两个后缀的公共前缀必定是某些height值中的最小值,而这个值如果最大则一定是这组中height中的最大值。

[代码]:

View Code
/*
ID:procedu6
PROB:theme
LANG:C++
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 23000

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

void makesa()
{
int i,j,m,len;
m=n<256?256:n;
memset(top,0,m*sizeof(int));
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,n*sizeof(int));
memcpy(sa,temp,n*sizeof(int));
if (j>n-1) break;
}
}

void lcp()
{
int i,j,k;
for (int j=rank[height[i=k=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 l=sa[0],r=sa[0];
for (int i=0;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 1;
}
return 0;
}

int solve()
{
int l=0,r=n,mid;
while (l<r)
{
mid=(l+r)/2+((l+r) & 1);
if (judge(mid)) l=mid; else r=mid-1;
}
return l;
}

int main()
{
freopen("theme.in","r",stdin);
freopen("theme.out","w",stdout);
while (scanf("%d",&n),n)
{
for (int i=0;i<n;i++)
scanf("%d",&f[i]);
for (int i=0;i<n-1;i++)
s[i]=f[i+1]-f[i];
makesa();
lcp();
//printf("asdfasdf");
ans=solve();
if (ans>=4) printf("%d\n",ans+1); else printf("0\n");
}
return 0;
}



你可能感兴趣的:(USACO)