1. 程式人生 > >poj 1743 [USACO5.1] Musical Theme (後綴數組+二分)

poj 1743 [USACO5.1] Musical Theme (後綴數組+二分)

-a algo inf int 不容易 names clas size 男人

洛谷P2743傳送門

題目大意:給你一個序列,求其中最長的一對相似等長子串

一對合法的相似子串被定義為:

1.任意一個子串長度都大於等於5

2.不能有重疊部分

3.其中一個子串可以在全部+/-某個值後和另一個串完全相同

還是老套路了,其實只要求出每一項和前一項的差值序列,這樣第三個問題可以被轉化為求最長不重疊的相同子串,最後把答案+1就行了

這道題竟然還是LTC大佬的男人八題!正解是後綴數組+二分,但據說可以n^2騙分過

思路是先求出height數組,對於排序後的字符串集合有一個神奇的性質,如果有某連續的幾個字符串height>=x,那麽這幾個字符串的公共前綴長度>=x,可以用Trie樹的思想很容易地證明

那現在就要解決第二個問題,不能有重疊部分

直接求解不容易,所以我們二分答案去做,每次二分出一個答案x,然後去height數組裏找,把連續的幾個height>=x字符串想象成一塊,那麽如果這個塊合法,這一塊內一定存在兩個字符串的sa[i]-sa[j]>=x,也就是在這一塊內找出sa[i]的最大最小值就行啦

復雜度O(nlogn)

傻了吧唧的把後綴數組敲錯了,調了半個小時

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N 20010
 5 #define
inf 0x3f3f3f3f 6 #define maxn 100 7 using namespace std; 8 9 int n,len; 10 int a[N],b[N],tr[N],rk[N],sa[N],hs[N],h[N]; 11 int gint() 12 { 13 int rett=0,fh=1;char c=getchar(); 14 while(c<0||c>9){if(c==-)fh=-1;c=getchar();} 15 while(c>=0&&c<=9){rett=(rett<<3
)+(rett<<1)+c-0;c=getchar();} 16 return rett*fh; 17 } 18 void clr() 19 { 20 memset(a,0,sizeof(a));memset(tr,0,sizeof(tr)); 21 memset(b,0,sizeof(b));memset(rk,0,sizeof(rk)); 22 memset(h,0,sizeof(h));memset(sa,0,sizeof(sa)); 23 memset(hs,0,sizeof(hs)); 24 } 25 bool check(int k,int x,int y){ 26 if(x+k>len||y+k>len) return 0; 27 else return (rk[x]==rk[y]&&rk[x+k]==rk[y+k])?1:0; 28 } 29 void get_sa() 30 { 31 int cnt=0,i=0; 32 for(i=1;i<=n;i++) hs[b[i]]++; 33 for(i=1;i<=2*maxn;i++) if(hs[i]) tr[i]=++cnt; 34 for(i=1;i<=2*maxn;i++) hs[i]+=hs[i-1]; 35 for(i=1;i<=n;i++) rk[i]=tr[b[i]],sa[hs[b[i]]--]=i; 36 for(int k=1;cnt<len;k<<=1) 37 { 38 for(i=1;i<=cnt;i++) hs[i]=0; 39 for(i=1;i<=len;i++) hs[rk[i]]++; 40 for(i=1;i<=cnt;i++) hs[i]+=hs[i-1]; 41 for(i=len;i>=1;i--) if(sa[i]>k) tr[sa[i]-k]=hs[rk[sa[i]-k]]--; 42 for(i=1;i<=k;i++) tr[len-i+1]=hs[rk[len-i+1]]--; 43 for(i=1;i<=len;i++) sa[tr[i]]=i; 44 for(i=1,cnt=0;i<=len;i++) tr[sa[i]]=check(k,sa[i],sa[i-1])?cnt:++cnt; 45 for(i=1;i<=len;i++) rk[i]=tr[i]; 46 } 47 } 48 void get_height() 49 { 50 for(int i=1;i<=len;i++){ 51 if(rk[i]==1) continue; 52 for(int j=max(1,h[rk[i-1]]-1);j;j++) 53 if(b[i+j-1]==b[sa[rk[i]-1]+j-1]) h[rk[i]]=j; 54 else break; 55 } 56 } 57 bool check(int ans) 58 { 59 int i=1,j=1; 60 for(i=1;i<=len;) 61 { 62 if(h[i]<ans) {i++;continue;} 63 int mi=sa[i-1],ma=sa[i-1]; 64 for(;h[i]>=ans&&i<=len;i++) 65 { 66 mi=min(mi,sa[i]); 67 ma=max(ma,sa[i]); 68 if(ma-mi>ans) return 1; 69 } 70 }return 0; 71 } 72 73 int main() 74 { 75 while(scanf("%d",&n)&&n!=0) 76 { 77 len=n; 78 clr(); 79 for(int i=1;i<=n;i++) 80 a[i]=gint(),b[i]=a[i]-a[i-1]+maxn; 81 get_sa(); 82 get_height(); 83 int l=0,r=n,ans=0; 84 while(l<=r){ 85 int mid=(l+r)>>1; 86 if(check(mid)) ans=mid,l=mid+1; 87 else r=mid-1; 88 } 89 if(ans<4) printf("0\n"); 90 else printf("%d\n",ans+1); 91 } 92 return 0; 93 }

poj 1743 [USACO5.1] Musical Theme (後綴數組+二分)