首页 > 代码库 > POJ 1743 (后缀数组+不重叠最长重复子串)
POJ 1743 (后缀数组+不重叠最长重复子串)
题目链接: http://poj.org/problem?id=1743
题目大意:楼教主の男人八题orz。一篇钢琴谱,每个旋律的值都在1~88以内。琴谱的某段会变调,也就是说某段的数可以加减一个旋律范围的值。问这个谱子内最长不重叠的重复部分大小。
解题思路:
网上题解已经泛滥的题。很多细节都被先辈大神总结了。
在当年后缀数组还不是热门的时候,这题确实是神题。
首先对于旋律变调的处理:
比如123,123,ans=3。
变调之后:456,123,ans=0?不ans=3。
所以不能使用旋律的初始值。应该取每个旋律前后的差值,这样就能保证某段无论怎么变调,都和原来一样。
不过这样就变成n-1个旋律,并且ans会-1,可以拿笔算几组看看。
对于取差值后的n-1个旋律,计算SA和LCP。 PS。很多人SA模板都是有问题的,并且推荐自动末尾补0的SA模板,不容易出现问题。
要求不重叠的重复部分大小,这里套用网上被传承N遍的奇葩结论:
将height数组分组,每组内的后缀之间的height都要大于len,如果每组内的后缀之间的最长公共前缀有大于len的而且这两个后缀的SA之差大于len就说明存在长度至少为len的不重复子串。求最长公共前缀就要用到height数组,因为这组中任意两个后缀的公共前缀必定是某些height值中的最小值,而这个值如果最大则一定是这组中height中的最大值。
由于SA数组按字典序来的,二分SA数组长度。如果len符合要求,则先记录。再向右找更大的,否则向左。注意二分的时候ans初始值为0,不然当n=1的时候,等于没有二分就return了一个ans。
#include "cstring"#include "cstdio"#include "string"#include "iostream"using namespace std;#define maxn 23000int n,r[maxn],tmp[maxn];template <class T>inline bool read(T &ret){ char c; int sgn; if(c=getchar(),c==EOF) return 0; //EOF while(c!=‘-‘&&(c<‘0‘||c>‘9‘)) c=getchar(); sgn=(c==‘-‘)?-1:1; ret=(c==‘-‘)?0:(c-‘0‘); while(c=getchar(),c>=‘0‘&&c<=‘9‘) ret=ret*10+(c-‘0‘); ret*=sgn; return 1;}struct Suffix{ int sa[maxn],rk[maxn],height[maxn]; int t[maxn],t2[maxn],c[maxn],m; void init() {m=200;} int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];} void build() { int i,k,p,*x=t,*y=t2; r[n++]=0; for (i=0; i<m; i++) c[i]=0; for (i=0; i<n; i++) c[x[i]=r[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 (k=1,p=1; k<n; k*=2,m=p) { for (p=0,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]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++; } n--; } void LCP() { int i,j,k=0; for (i=1; i<=n; i++) rk[sa[i]]=i; for (i=0; i<n; i++) { if (k) k--; j=sa[rk[i]-1]; while (r[i+k]==r[j+k]) k++; height[rk[i]]=k; } } bool judge(int len) { int l=sa[1],r=sa[1]; for(int i=2;i<=n;i++) { if(height[i]<len) { l=sa[i];r=sa[i]; continue; } l=min(l,sa[i]); r=max(r,sa[i]); if(r-l>len) return true; } return false; } int BinarySearch() { int l=0,r=n,mid,ans=0;//注意ans=0,不然当n=1的时候返回的是没赋值的ans while(l<=r) { int mid=l+(r-l)/2; if(judge(mid)) {ans=mid;l=mid+1;} else r=mid-1; } return ans; }};int main(){ //freopen("in.txt","r",stdin); //freopen("out1.txt","w",stdout); while(read(n)&&n) { for(int i=0; i<n; i++) read(tmp[i]); for(int i=0; i<n-1; i++) {r[i]=tmp[i+1]-tmp[i]+90;} Suffix a; a.init(); a.build(); a.LCP(); int ans=a.BinarySearch(); if(ans<4) printf("0\n"); else printf("%d\n",ans+1); }}
13560509 | neopenx | 1743 | Accepted | 840K | 407MS | C++ | 2761B | 2014-10-24 10:20:38 |
POJ 1743 (后缀数组+不重叠最长重复子串)