1. 程式人生 > >POJ 3261 Milk Patterns ( 後綴數組 && 出現k次最長可重疊子串長度 )

POJ 3261 Milk Patterns ( 後綴數組 && 出現k次最長可重疊子串長度 )

b+ 重疊 可能性 nbsp 如果 ide sca 技術 print

題意 : 給出一個長度為 N 的序列,再給出一個 K 要求求出出現了至少 K 次的最長可重疊子串的長度

分析 : 後綴數組套路題,思路是二分長度再對於每一個長度進行判斷,判斷過程就是對於 Height 數組進行限定長度的分組策略,如果有哪一組的個數 ≥ k 則說明可行!

技術分享圖片

分組要考慮到一個事實,對於每一個後綴,與其相匹配能夠產生最長的LCP長度的串肯定是在後綴數組中排名與其相鄰。

一開始對分組的理解有誤,所以想了一個錯誤做法 ==>

遍歷一下 Height 將值 ≥ (當前二分長度) 的做一次貢獻即 cnt++ ,若最後 cnt ≥ K 說明可行。當然這個肯定是炸了.......

下面說說我對於 Height 分組的理解吧,就看上面的圖,如果當前 K == 2,那麽第一組的含義是什麽?換句話說就是為什麽那麽些個後綴要屬於一組?可以看出第一組裏面的 Height 值都不會小於 K ,實際的意義呢應當是第一組裏面的有一個長度為 2 (不小於K)的共同前綴,即 “aa” ,那麽是不是 “aa” 這個子串可重疊地出現了 cnt 次(cnt為第一組的後綴個數),可能你已經有點體會到分組的意義了!那麽有沒有可能有些前綴是 “aa” 但是沒有被分進第一組呢?看見上面紅字描述的事實麽?根據上面的那個事實,而且 Height 的下標是根據排名有序的這個特點(有序的意思就是從小到大遍歷 Height 實際傳進去的下標就是排名!即 Height[i],i是表示第 i 名的後綴),我們就知道這樣的事情不會發生,且分出來的組肯定的“連續的塊”,即不會有這一組的元素在其他地方的可能性!

技術分享圖片
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 1e6 + 10;

int sa[maxn], s[maxn], wa[maxn], Ws[maxn], wv[maxn], wb[maxn];
int Rank[maxn], height[maxn];

bool 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; 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 (std::swap(x, y), 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++; } } void calheight(int r[], int sa[], int n) { int i, j, k = 0; for (i = 1; i <= n; ++i) Rank[sa[i]] = i; 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++); } bool IsOk(int len, int n, int aim) { int cnt = 1; // for(int i=2; i<=n; i++){ //錯誤的! // if(height[i] >= len) // if(++cnt >= aim) // return true; // }return false; for(int i=2; i<=n; i++){ if(height[i] >= len){ if(++cnt >= aim) return true; } else cnt = 1; }return false; } int arr[maxn]; int main(void) { int N, K; while(~scanf("%d %d", &N, &K)){ for(int i=0; i<N; i++) scanf("%d", &arr[i]); da(arr, sa, N+1, 1000005); calheight(arr, sa, N); int L = 0, R = N, ans = -1; while(L <= R){ int mid = L + ((R-L)>>1); if(IsOk(mid, N, K)) ans = mid, L = mid + 1; else R = mid - 1; } ans==-1? puts("0") : printf("%d\n", ans); } return 0; }
View Code

POJ 3261 Milk Patterns ( 後綴數組 && 出現k次最長可重疊子串長度 )