poj 1743
阿新 • • 發佈:2018-09-26
max 一段 tac %d 答案 pan () continue std
激動ing...我完成了1/8的男人...
其實不難,但我交了3頁WA,原因是我為防RE特判n=1直接輸出,並沒有讀入!!!
題意:給定一個長為n的序列,定義該序列的兩個子序列(連續)相似,當且僅當這兩個子序列長度相同且互不重疊,而且這兩個子序列每個序列內部相鄰元素前後做差,將所有差值排列成一個序列,這兩個子序列得出的序列完全相同,現在想求這樣的子序列最長的長度
舉個例子:序列1 2 5和4 5 8是相似的,因為做差後的序列都是1 3
然後我們考慮怎麽解決這個問題
主體思想:後綴數組+二分答案
其實有個思想是顯然的,就是對於原序列兩兩做差,那麽問題會簡化成求一個序列中完全相同且互不相交的兩個子段最長有多長
利用後綴數組優秀的性質,我們可以求出一個height數組,其中height[i]表示排名為i和i-1的兩個後綴的最長公共前綴長度
至於height數組怎麽求,可以參考網上其他博客,這裏先略過
我們重點談一談如何利用height數組的性質解決本題
首先,我們可以二分答案,即二分可能的長度
然後我們進行檢驗:
設二分的答案為v
枚舉字符串的排名(由於排名為1的字符串height為0,所以可以從2開始),然後檢驗他的height值,如果我們有長度超過v的一段height值大於等於v,那麽就是合法的
原因:
如圖所示,如果我們有從st到ed的一段height值都>=v,而且ed-st>v,那麽我們一定能找到一個以st為起點和以ed為起點的,長度為v的子段相同
這樣也就實現了檢驗
代碼:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; int sa[30005]; int heit[30005]; int rank[30005]; int f1[30005]; int f2[30005]; intf3[30005]; int a[30005]; int s[30005]; int has[30005]; int l,m=200; void turnit() { memcpy(f3,f1,sizeof(f3)); memcpy(f1,f2,sizeof(f1)); memcpy(f2,f3,sizeof(f2)); } void init() { memset(sa,0,sizeof(sa)); memset(has,0,sizeof(has)); memset(s,0,sizeof(s)); memset(a,0,sizeof(a)); memset(heit,0,sizeof(heit)); memset(rank,0,sizeof(rank)); memset(f1,0,sizeof(f1)); memset(f2,0,sizeof(f2)); memset(f3,0,sizeof(f3)); } void get_hei() { int f=0; for(int i=1;i<=l;i++) { if(rank[i]==1) { continue; } int j=sa[rank[i]-1]; if(f) { f--; } while(s[i+f]==s[j+f]) { f++; } heit[rank[i]]=f; } } void get_sa() { for(int i=1;i<=l;i++) { f1[i]=s[i]; has[f1[i]]++; } for(int i=2;i<=m;i++) { has[i]+=has[i-1]; } for(int i=l;i>=1;i--) { sa[has[f1[i]]--]=i; } for(int k=1;k<=l;k<<=1) { int tot=0; for(int i=l-k+1;i<=l;i++) { f2[++tot]=i; } for(int i=1;i<=l;i++) { if(sa[i]>k) { f2[++tot]=sa[i]-k; } } for(int i=1;i<=m;i++) { has[i]=0; } for(int i=1;i<=l;i++) { has[f1[i]]++; } for(int i=1;i<=m;i++) { has[i]+=has[i-1]; } for(int i=l;i>=1;i--) { sa[has[f1[f2[i]]]--]=f2[i]; f2[i]=0; } turnit(); f1[sa[1]]=1; tot=1; for(int i=2;i<=l;i++) { if(f2[sa[i]]==f2[sa[i-1]]&&f2[sa[i]+k]==f2[sa[i-1]+k]) { f1[sa[i]]=tot; }else { f1[sa[i]]=++tot; } } if(tot==l) { break; } m=tot; } for(int i=1;i<=l;i++) { rank[sa[i]]=i; } get_hei(); } bool check(int v) { int minx=0x3f3f3f3f; int maxx=-0x3f3f3f3f; for(int i=2;i<=l;i++) { if(heit[i]>=v) { minx=min(minx,min(sa[i],sa[i-1])); maxx=max(maxx,max(sa[i],sa[i-1])); if(maxx-minx>v) { return 1; } }else { minx=0x3f3f3f3f; maxx=-0x3f3f3f3f; } } return 0; } void divi() { int lq=0,rq=l/2; int ans; while(lq<=rq) { int mid=(lq+rq)>>1; if(check(mid)) { ans=mid; lq=mid+1; }else { rq=mid-1; } } if(ans<4) { printf("0\n"); }else { printf("%d\n",ans+1); } } int main() { // freopen("data.txt","r",stdin); while(1) { scanf("%d",&l); if(l==0) { return 0; } init(); for(int i=1;i<=l;i++) { scanf("%d",&a[i]); } if(l==1) { printf("0\n"); continue; } l--; for(int i=1;i<=l;i++) { s[i]=a[i+1]-a[i]+100; } m=200; get_sa(); divi(); } return 0; }
poj 1743