POJ 1743 Musical Theme(後綴數組+二分答案)
阿新 • • 發佈:2019-03-21
spa 二分 min 題目 cal get true %d 一段
題意
長度為 \(n\) 由數字構成的串,求一段最長滿足以下要求的子串:
- 長度至少為 \(5\)
- 在其他位置有一個不相交的等長子串滿足原串或原串加上或減去一個數後與之完全相同。
\(1\leq 20000 \leq n\)
思路
不難看出題目要求的是滿足兩兩數之差相同的串,那麽直接對原串進行差分,剩下的就是求兩個串至少間隔為 \(1\) 的相等串的最長長度。這顯然是滿足單調性的,那麽二分這個長度,然後掃後綴數組,對於每一個 \(H\) 大於等於這個長度的區間,看一看有沒有符合要求的串,具體實現看代碼。
代碼
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;} template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;} typedef long long ll; const int N=2e4+5; int sa[N],rk[N],H[N],tmp[3][N]; int s[N]; int n; void get_SA(int m) { memset(tmp,0,sizeof(tmp)); int *x=tmp[0],*y=tmp[1],*c=tmp[2]; FOR(i,1,m)c[i]=0; FOR(i,1,n)c[x[i]=s[i]]++; FOR(i,2,m)c[i]+=c[i-1]; DOR(i,n,1)sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { int p=0; FOR(i,n-k+1,n)y[++p]=i; FOR(i,1,n)if(sa[i]>k)y[++p]=sa[i]-k; FOR(i,1,m)c[i]=0; FOR(i,1,n)c[x[y[i]]]++; FOR(i,2,m)c[i]+=c[i-1]; DOR(i,n,1)sa[c[x[y[i]]]--]=y[i]; std::swap(x,y); p=x[sa[1]]=1; FOR(i,2,n)x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p; if(p==n)break; m=p; } FOR(i,1,n)rk[sa[i]]=i; int k=0; FOR(i,1,n) { if(k)k--; if(rk[i]==1)continue; int j=sa[rk[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++; H[rk[i]]=k; } } bool check(int k) { int Mx=-1e9,Mi=1e9; FOR(i,1,n) { chk_max(Mx,sa[i]),chk_min(Mi,sa[i]); if(Mx-Mi>=k+1)return true; if(H[i+1]<k)Mx=-1e9,Mi=1e9; } return false; } int main() { while(scanf("%d",&n),n) { FOR(i,1,n)scanf("%d",&s[i]); FOR(i,1,n-1)s[i]=s[i+1]-s[i]+100; n--; get_SA(200); int l=3,r=n; while(l<r) { int mid=(l+r+1)>>1; if(check(mid)) l=mid; else r=mid-1; } printf("%d\n",l>=4?l+1:0); } return 0; }
POJ 1743 Musical Theme(後綴數組+二分答案)