POJ 1743:后缀数组求 不重叠最长重复子串

来源:互联网 发布:最强nba球员数据对比 编辑:程序博客网 时间:2024/05/17 04:20

题意:给出一个数列,每个数字在[ 1 , 88 ]之间,每个数字对应钢琴的一个按键。定义相同的旋律为:[ i , i+len-1 ]区间 和 [ j , j+len-1 ]区间对应位置每个字符,差值恒定相等,且这两个区间不想交。就是说第一个区间每个数字加上一个delta,会变成第二个区间。求最长的旋律长度,如果小于5,输出0.否则输出最长的相同旋律的长度。


题解:考虑查分数列。两段旋律相同,意味着他们的查分区间相同。于是问题转化为求最长不重叠重复子串,可以用二分+后缀数组。这个题目要求的是原序列不想交,那么转化为差分序列之后,应该是两段序列中间至少相隔一个元素。


二分的check:检查是否存在两个子串,他们“不相交”且公共前缀>=K,那么顺序扫描height数组,把height值>=k的放到一起统计,要保证其中最大的sa-最小的sa>k,才能保证“不相交”。


Code:

#include<iostream>#include<cstring>#include<algorithm>#include<cstdio>#include<queue>using namespace std;#define rank rkconst int MAX = 2e4+1000;int cntA[MAX],cntB[MAX],A[MAX],B[MAX],tsa[MAX],SA[MAX],rank[MAX],h[MAX],ch[MAX];int n;const int INF = 0x3f3f3f3f;void init(){}void input(){int now;int pre;scanf("%d",&pre);n--;for (int i=1;i<=n;i++){scanf("%d",&now);ch[i] = now-pre+200;pre=now;}ch[n+1]='\0';}void get_SA(){for (int i=0;i<MAX;i++) cntA[i]=0;for (int i=1;i<=n;i++) cntA[ch[i]]++;for (int i=1;i<MAX;i++) cntA[i]+=cntA[i-1];for (int i=n;i>=1;i--) SA[cntA[ch[i]]--]=i;rank[SA[1]]=1;for (int i=2;i<=n;i++){rank[SA[i]]=rank[SA[i-1]];if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++;}for (int step = 1;rank[SA[n]]<n;step<<=1){for (int i=0;i<=n;i++) cntA[i]=cntB[i]=0;for (int i=1;i<=n;i++){cntA[A[i]=rank[i]]++;cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;}for (int i=1;i<=n;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];for (int i=n;i>=1;i--) tsa[cntB[B[i]]--] = i;for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i];rank[SA[1]]=1;for (int i=2;i<=n;i++){rank[SA[i]]=rank[SA[i-1]];if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++;}}}void get_Height(){for (int i=1,j=0;i<=n;i++){if (j)j--;while (ch[i+j]==ch[SA[rank[i]-1]+j])j++;h[rank[i]]=j;}}bool check(int K){if (K==0){return true;}int minSA,maxSA;for (int i=2;i<=n;i++){if (h[i]>=K){minSA = min(minSA,min(SA[i],SA[i-1]));maxSA = max(maxSA,max(SA[i],SA[i-1]));if (maxSA-minSA>K){return true;}}else{minSA = INF;maxSA = -INF;}}return false;}void solve(){get_SA();get_Height();int l=0,r=n/2;while (r-l>1){int mid = l+r >>1;if (check(mid)){l=mid;}else{r=mid;}}int ans;if (check(r)){ans=r+1;}else{ans=l+1;}if (ans>=5){cout<<ans<<endl;}else{cout<<0<<endl;}}int main(){while (scanf("%d",&n)!=EOF&&n){if (n==1){scanf("%d",&n);cout<<0<<endl;continue;}init();input();solve();}return 0;}


阅读全文
0 0
原创粉丝点击