1. 程式人生 > >hdu3518(後綴數組)

hdu3518(後綴數組)

字串 splay ++ 最大值 pen 基數 hide amp pre

題目鏈接: http://acm.hdu.edu.cn/showproblem.php?pid=3518

題意: 給出一個字符串, 問其中有多少字串出現了兩次以上(計算次數時不能彼此覆蓋, 如 "aaaa" 中 "aa" 出現了兩次而非三次).

思路: 後綴數組/字典樹

後綴數組解法, 題目所求即使用後綴中出現兩次以上的前綴數目. 可以枚舉前綴長度, 將滿足條件的前綴累進答案中. 在 SA 數組中, 具有相同前綴的後綴肯定是在一個連續塊中的. 可以用 height 數組的性質來區分當前長度有哪些前綴塊. 註意滿足條件的前綴塊中至少存在兩個彼此不覆蓋的前綴.

代碼:

技術分享
 1 #include <iostream>
 2
#include <stdio.h> 3 #include <string.h> 4 #define rank Rank 5 using namespace std; 6 7 const int MAXN = 1e4 + 10; 8 char str[MAXN]; 9 int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN], a[MAXN]; 10 11 bool cmp(int *f, int x, int y, int w){ 12 return f[x] == f[y] && f[x + w] == f[y + w];
13 } 14 15 void get_SA(int *s, int n, int m){ 16 for(int i = 0; i < m; i++) sum[i] = 0; 17 for(int i = 0; i < n; i++) sum[rank[i] = s[i]]++; 18 for(int i = 1; i < m; i++) sum[i] += sum[i - 1]; 19 for(int i = n - 1; i >= 0; i--) SA[--sum[rank[i]]] = i; 20 for(int len = 1; len <= n; len <<= 1
){ 21 int p = 0; 22 for(int i = n - len; i < n; i++) tp[p++] = i;//後面i個數沒有第二關鍵字,即第二關鍵字為空,所以最小 23 for(int i = 0; i < n; i++){ 24 if(SA[i] >= len) tp[p++] = SA[i] - len; 25 } 26 //tp[i]存儲按第二關鍵字排序第i的下標 27 //對第二關鍵字排序的結果再按第一關鍵字排序,和長度為1的情況類似 28 for(int i = 0; i < m; i++) sum[i] = 0; 29 for(int i = 0; i < n; i++) sum[rank[tp[i]]]++; 30 for(int i = 1; i < m; i++) sum[i] += sum[i - 1]; 31 for(int i = n - 1; i >= 0; i--) SA[--sum[rank[tp[i]]]] = tp[i]; 32 //根據SA和rank數組重新計算rank數組 33 swap(rank, tp);//交換後tp指向舊的rank數組 34 p = 1; 35 rank[SA[0]] = 0; 36 for(int i = 1; i < n; i++){ 37 rank[SA[i]] = cmp(tp, SA[i - 1], SA[i], len) ? p - 1 : p++; 38 } 39 if(p >= n) break; 40 m = p;//下次基數排序的最大值 41 } 42 //求height 43 int k = 0; 44 n--; 45 for(int i = 0; i <= n; i++) rank[SA[i]] = i; 46 for(int i = 0; i < n; i++){ 47 if(k) k--; 48 int j = SA[rank[i] - 1]; 49 while(s[i + k] == s[j + k]) k++; 50 height[rank[i]] = k; 51 } 52 } 53 54 int main(void){ 55 while(~scanf("%s", str)){ 56 if(str[0] == #) break; 57 int len = strlen(str), sol = 0; 58 for(int i = 0; i < len; i++) a[i] = str[i]; 59 a[len] = 0; 60 get_SA(a, len + 1, 128); 61 for(int i = 1; i <= len / 2; i++){ 62 int l = MAXN, r = 0; 63 for(int j = 2; j <= len; j++){ 64 if(height[j] >= i){ 65 l = min(l, min(SA[j], SA[j - 1])); 66 r = max(r, max(SA[j], SA[j - 1])); 67 }else{ 68 if(r - l >= i) sol++; 69 l = MAXN; 70 r = 0; 71 } 72 } 73 if(r - l >= i) sol++; 74 } 75 printf("%d\n", sol); 76 } 77 return 0; 78 }
View Code

hdu3518(後綴數組)