您的位置:首页 > 其它

POJ 1743 Musical Theme

2018-02-27 15:50 225 查看
题目

给n个数组成的串,求是否有多个“相似”且不重叠的子串的长度大于等于5,两个子串相似当且仅当长度相等且每一位的数字差都相等。

Input
The input contains several test cases. The first line of each test case contains the integer N. The following n integers represent the sequence of notes.
The last test case is followed by one zero.

Output

For each test case, the output file should contain a single line
with a single integer that represents the length of the longest theme.
If there are no themes, output 0.

Sample Input
30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
82 78 74 70 66 67 64 60 65 80
0

Sample Output
5
不考虑相似的定义,先考虑相同
即不可重叠最长重复子串
先求出h数组,二分答案k
然后分成若干组,每组的h都大于k,如果该区间内Max_SA-Min_SA>=k
就说明存在长为k的[b]不可重叠最长重复子串[/b]
本题从相同变成"相似"
其实把原数组[b]差分,这样得出的新串如果有两个长度为n的子串相同,那么它们对应在原串的长度n+1的子串也就相似[/b]
也就转化为求差分数组[b]不可"相交"最长重复子串[/b]
即两个子串至少要隔一个数
因为如果两个子串靠在一起这样反应到原串那两个子串各自的首尾是重合的
记得h[i]在最后加一个0,不然最后一组不会统计

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int s[200001],c[200001],SA[200001],h[200001],n,m,x[200001],y[200001],rank[200001],Max,Min,ans;
void radix_sort()
{int i;
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];
}
void build_SA()
{int i,j,k,p;
for (i=0;i<n;i++)
x[i]=s[i],y[i]=i;
m=1000;
radix_sort();
for (k=1;k<=n;k<<=1)
{
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;
radix_sort();
p=1;swap(x,y);
x[SA[0]]=0;
for (i=1;i<n;i++)
x[SA[i]]=((y[SA[i]]==y[SA[i-1]])&&((SA[i]+k<n?y[SA[i]+k]:-1)==(SA[i-1]+k<n?y[SA[i-1]+k]:-1)))?p-1:p++;
if (p>=n) break;
m=p;
}
for (i=0;i<n;i++)
rank[SA[i]]=i;
int L=0;
for (i=0;i<n;i++)
if (rank[i]>0)
{
if (L>0) L--;
j=SA[rank[i]-1];
while (i+L<n&&j+L<n&&(s[j+L]==s[i+L])) L++;
h[rank[i]]=L;
}
}
bool check(int mid)
{int i;
Max=-1e9;Min=1e9;
h
=0;
for (i=1;i<=n;i++)
{
if (h[i]>=mid)
{
Max=max(max(SA[i],SA[i-1]),Max);
Min=min(min(SA[i],SA[i-1]),Min);
}
else
{
if (Max-Min>mid)
return 1;
Max=-1e9;Min=1e9;
}
}
return 0;
}
int main()
{int i,now,last;
while (cin>>n&&n)
{
scanf("%d",&last);
if (n==1)
{
cout<<0<<endl;
continue;
}
for (i=0;i<n-1;i++)
{
scanf("%d",&now);
s[i]=now-last+88;
last=now;
}
n--;
s
=0;
build_SA();
int l=0,r=n/2;
ans=0;
while (l<=r)
{
int mid=(l+r+1)/2;
if (check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
if (ans<4) printf("0\n");
else
printf("%d\n",ans+1);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: