對於尾碼數組強烈建議看國家集訓隊 04年和09年的兩篇關於尾碼數組的論文
尾碼數組非常強大,但是這麼強大的資料結構理解起來不是那麼容易的,裡面涉及到好多知識點,要想徹底理解尾碼數組,表示需要一點功底才行
對於尾碼數組具體請看國家集訓對09年和04年的兩篇大牛關於尾碼數組的論文,兩篇結合起來看效果會更好,可能看第一第二遍的時候不是怎麼理解
這個是肯定的,相信那兩個大牛寫這兩篇論文的時候花的時間也不止幾天吧,人家花幾天寫的論文給你一下子就看懂了,你當人家寫的是小說啊,一般
像這種複雜的資料結構要多看幾遍,甚至當你認為你已經看懂之後不妨再多看幾遍,這樣會有更深的理解!
尾碼數組主要是兩個 一個是尾碼數組,一個是名次數組,這兩個數組相互搭配完成尾碼數組的強大功能
尾碼數組sa儲存的是拍名,也就是下標表示排名,內容表示排這個名的是第幾個尾碼
rank數組正好相反,是第下標個尾碼的排名
然後用倍增演算法實現對尾碼數組的構造,我開始看的時候沒看懂,後來稍微懂了之後發現這個演算法用的也不是什麼高深的演算法
對於這個演算法的理解還是要在對字串比較的過程有一定的理解,這個題目關鍵就是按照字元出現的位置對字元賦予了類似整數
比較的一個權值,在前面的字元大小最總要,也就是個十百千位那樣,先求一個長度的排名,然後根據一個的求兩個的,一次往後
最後完成整個尾碼數組的構造
構造完成之後這個尾碼數組好像還不能幹什麼事情,接下來又引進了height數組(具體定義請看論文),這個height數組儲存的是
排名相鄰的兩個尾碼的最長首碼,現在我們關鍵任務是怎樣在最短時間內構造出這個height數組,論文上給了O(n)的複雜度,
這個求法類似於動態規劃的思想,利用前面的結果,在其基礎之上跟新現在的結果
完了之後就是這個題目的內容了
這個題目的思想是二分枚舉,和判斷重複,然後按照height的值進行分組,分組完成之後判斷組內最大位置和最小位置的差值是否
滿足題目條件,判斷是否滿足題目要求,具體為什麼分組和最大位置最小位置差就能確定,看看下面代碼注釋
#include <iostream>#include <stdio.h>#include <algorithm>#include <math.h>using namespace std;#define maxn 21000#define ws ws1int wa[maxn],wb[maxn],wv[maxn],ws[maxn];int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}void da(int *r,int *sa,int n,int m){int i,j,p,*x=wa,*y=wb,*t;for(i=0;i<m;i++) ws[i]=0;for(i=0;i<n;i++) ws[x[i]=r[i]]++;for(i=1;i<m;i++) ws[i]+=ws[i-1];for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;for(j=1,p=1;p<n;j*=2,m=p){for(p=0,i=n-j;i<n;i++) y[p++]=i;for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;for(i=0;i<n;i++) wv[i]=x[y[i]];for(i=0;i<m;i++) ws[i]=0;for(i=0;i<n;i++) ws[wv[i]]++;for(i=1;i<m;i++) ws[i]+=ws[i-1];for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;}return;}int rank[maxn],height[maxn];void calheight(int *r,int *sa,int n){int i,j,k=0;for(i=1;i<=n;i++) rank[sa[i]]=i;//在計算height的時候順便就把rank計算出來了,反正也要用for(i=0;i<n;height[rank[i++]]=k)for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);return;}int rec[maxn],rec1[maxn],sa[maxn];int n;bool check(int m){ int mmax=0,mmin=n; /*這裡說的就是論文上對height數組進行分組 *這個分組是按照順序來分組的,也就是把整個height數組按照比m大和沒有m大分成一段一段,如果在 *在某一段記憶體在答案就說明成立 *還是按照自己的理解解釋一下這裡為什麼按照這樣分組就可以,為什麼是這個分組區間的最左邊和最右邊的不相交就可以 *想一想如果一個區間的height值都比m大,那麼這麼多組尾碼的前m個一定是相同的,因為是前一組前m個等於後一組m個, *而後一的前m個又等於後後一組的,這樣推出來,所以這樣分組是正確的 */ for(int i=1;i<=n;i++) { if(height[i]<m) { mmax=sa[i]; mmin=sa[i]; } else { mmax=max(mmax,max(sa[i],sa[i-1])); mmin=min(mmin,min(sa[i],sa[i-1])); if(mmax-mmin>m) return true; } } return false;}int main(){ int i,j,k; int left,right; int mid; int answer; while(scanf("%d",&n),n) { for(i=0;i<n;i++) scanf("%d",&rec1[i]); for(i=0;i<n-1;i++) rec[i]=rec1[i+1]-rec1[i]+100; n--; rec[n]=0; da(rec,sa,n+1,200);//這裡是n+1 因為看這個函數裡面是 < n 的 calheight(rec,sa,n);//注意這裡面是 n 了因為看函數裡面是 <=n 的,所以這裡要注意 right=-1; for(i=0;i < n ;i++) if(right < height[i]) right=height[i]; //for(i=0;i<=n;i++) // printf("%d ",height[i]); // printf("\n"); if(!check(4)) { printf("0\n"); continue; } left=4; answer=4; while(left <= right) { mid=(left+right)>>1; if(check(mid)) { answer=mid; left=mid+1; } else { right=mid-1; } } printf("%d\n",answer+1); } return 0;}