POJ-3693 Maximum repetition substring 后缀数组
題目鏈接:http://poj.org/problem?id=3693
求字符串的重復次數最多的且字典序最小的字串。
很不錯的題目。羅穗騫大牛論文的模板題,摘了Neo / Add ~0U>>1大牛的詳細題解,如下:
首先求第一問最大重復數。從N的范圍來看O(N^2)雖不靠譜,但是起碼能帶來些有用的啟示。方法有二,一是枚舉開頭位置求重復長度;二是枚舉重復長度求開頭位置。
第一種方法求最大重復數的方法MS也只有枚舉重復長度然后去判……所以說我們從第二個方法入手。O(N^2)的方法是再枚舉開頭位置。我們來考慮一下能不能少枚舉一些開頭位置——更確切地說,能不能只枚舉一些特殊位置,設當前枚舉的長度為L,能不能只枚舉0,L,2L,……這樣的位置。見下圖:
? ? ? ? ? ? ? ? ? ? ? ? ?
首先明確,枚舉長度Len的時候,從一個位置求出的Lcp值表明從此處開始重復數為floor(Lcp / Len) + 1。
然后容易發現的問題就是,可能從某個枚舉位置向前移動“一點”能夠多一個周期,從而使重復數增加。從圖上來看,這種狀況發生的條件就是Lcp比滿足這個重復數“必需的Lcp”要多,也就是Lcp % Len?≠ 0。不難發現,這時候我們向前移動Len - Lcp % Len的話,如果有下一個周期就會進去,否則也不會引起新的變化。所以說這種方法可行。
這樣的話復雜度為n(1 + 1/2 + 1/3 + ... + 1/n) = O(nlogn)。有一個顯而易見的優化,那就是只需要枚舉到floor(n / 2)即可。但是后面的部分也少不了多少……
第二問是求最小字典序的答案。我采用了這樣一種方法,那就是在第一步中記錄能夠達到最大重復數的重復長度的集合,然后按照sa[1],sa[2],sa[3],……的順序去暴力枚舉,每到一個位置就從小到大地判重復長度的集合中有沒有可行的,如果有一個可行的就立即輸出結果并退出。這樣的做法能夠保證正確性,雖然最壞情況下仍然有可能退化到O(n^2),但是要構造這樣一組數據是比較困難的(得一直枚舉到最后一個后綴,還得從1到Len / 2都能滿足最大重復數)。對于非特殊構造的數據這一步的復雜度幾乎是O(n),于是這個題就可以水過了。
我的代碼:
1 //STATUS:C++_AC_422MS_11232KB 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<string.h> 5 #include<math.h> 6 #include<iostream> 7 #include<string> 8 #include<algorithm> 9 #include<vector> 10 #include<queue> 11 #include<stack> 12 #include<map> 13 using namespace std; 14 #define LL __int64 15 #define pii pair<int,int> 16 #define mem(a,b) memset(a,b,sizeof(a)) 17 #define lson l,mid,rt<<1 18 #define rson mid+1,r,rt<<1|1 19 #define PI acos(-1.0) 20 const int N=100010,INF=0x3f3f3f3f,MOD=10000,STA=8000010; 21 //const LL LNF=0x3f3f3f3f3f3f3f3f; 22 const double DNF=1e13; 23 // 24 inline int Max(int a,int b){return a>b?a:b;} 25 inline int Min(int a,int b){return a<b?a:b;} 26 void swap(int& a,int& b){int t=a;a=b;b=t;} 27 void swap(LL& a,LL& b){LL t=a;a=b;b=t;} 28 // 29 30 char s[N]; 31 int d[N][20]; 32 int num[N]; 33 int sa[N],t1[N],t2[N],c[N],rank[N],height[N]; 34 int n,m; 35 36 void build_sa(int s[],int n,int m) 37 { 38 int i,k,p,*x=t1,*y=t2; 39 //第一輪基數排序 40 for(i=0;i<m;i++)c[i]=0; 41 for(i=0;i<n;i++)c[x[i]=s[i]]++; 42 for(i=1;i<m;i++)c[i]+=c[i-1]; 43 for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i; 44 for(k=1;k<=n;k<<=1){ 45 p=0; 46 //直接利用sa數組排序第二關鍵字 47 for(i=n-k;i<n;i++)y[p++]=i; 48 for(i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 49 //基數排序第一關鍵字 50 for(i=0;i<m;i++)c[i]=0; 51 for(i=0;i<n;i++)c[x[y[i]]]++; 52 for(i=1;i<m;i++)c[i]+=c[i-1]; 53 for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 54 //根據sa和x數組計算新的x數組 55 swap(x,y); 56 p=1;x[sa[0]]=0; 57 for(i=1;i<n;i++) 58 x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++; 59 if(p>=n)break; //已經排好序,直接退出 60 m=p; //下次基數排序的最大值 61 } 62 } 63 64 void getHeight(int s[],int n) 65 { 66 int i,j,k=0; 67 for(i=0;i<=n;i++)rank[sa[i]]=i; 68 for(i=0;i<n;i++){ 69 if(k)k--; 70 j=sa[rank[i]-1]; 71 while(s[i+k]==s[j+k])k++; 72 height[rank[i]]=k; 73 } 74 } 75 76 void rmq_init(int a[]) 77 { 78 int i,j; 79 for(i=1;i<=n;i++)d[i][0]=a[i]; 80 for(j=1;(1<<j)<=n;j++){ 81 for(i=1;i+(1<<j)-1<=n;i++){ 82 d[i][j]=Min(d[i][j-1],d[i+(1<<(j-1))][j-1]); 83 } 84 } 85 } 86 87 int rmq(int l,int r) 88 { 89 int k=0; 90 while((1<<(k+1))<=r-l+1)k++; 91 return Min(d[l][k],d[r-(1<<k)+1][k]); 92 } 93 94 int lcp(int a,int b) 95 { 96 if(a==b)return n-a; 97 int ra=rank[a],rb=rank[b]; 98 if(ra>rb)swap(ra,rb); 99 ra++; 100 return rmq(ra,rb); 101 } 102 103 int main() 104 { 105 // freopen("in.txt","r",stdin); 106 int i,j,sz=1,hig,ans[N],cnt,fre,wide,t,ss,ok; 107 while(~scanf("%s",s) && (s[0]!='#' || s[1]) ) 108 { 109 n=strlen(s); 110 for(i=0;i<n;i++){ 111 num[i]=s[i]-'a'+1; 112 } 113 num[n]=0; 114 m=27; 115 build_sa(num,n+1,m); 116 getHeight(num,n); 117 rmq_init(height); 118 119 hig=0; 120 for(i=1;i<=n/2;i++){ 121 for(j=0;j+i<n;j+=i){ 122 wide=lcp(j,j+i); 123 fre=wide/i+1; 124 t=j-(i-wide%i); 125 if(t>=0 && wide%i){ 126 wide=lcp(t,t+i); 127 fre=Max(fre,wide/i+1); 128 } 129 if(fre>hig){ 130 hig=fre; 131 cnt=0; 132 ans[cnt++]=i; 133 } 134 else if(fre==hig){ 135 ans[cnt++]=i; 136 } 137 } 138 } 139 140 ok=0; 141 for(i=1;i<=n;i++){ 142 for(j=0;j<cnt;j++){ 143 if(sa[i]+ans[j]>=n)continue; 144 wide=lcp(sa[i],sa[i]+ans[j]); 145 if(wide/ans[j]+1==hig){ 146 ss=sa[i]; 147 s[ss+hig*ans[j]]=0; 148 ok=1; 149 break; 150 } 151 } 152 if(ok)break; 153 } 154 155 printf("Case %d: %s\n",sz++,s+ss); 156 } 157 return 0; 158 }?
轉載于:https://www.cnblogs.com/zhsl/archive/2013/04/24/3040629.html
總結
以上是生活随笔為你收集整理的POJ-3693 Maximum repetition substring 后缀数组的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java二元运算和三元运算速度测试
- 下一篇: python实现微信自动回复_pytho