1. 程式人生 > >[TyvjP1515] 子串統計 [luoguP2408] 不同子串個數(後綴數組)

[TyvjP1515] 子串統計 [luoguP2408] 不同子串個數(後綴數組)

eight height gif nbsp getchar() aac ble %d org

Tyvj傳送門

luogu傳送門

經典題

統計一個字符串中不同子串的個數

一個字符串中的所有子串就是所有後綴的前綴

先求出後綴數組,求出後綴數組中相鄰兩後綴的 lcp

那麽按照後綴數組中的順序遍歷求解

每一個後綴 suffix(sa[i]) 對於答案的貢獻為 len - sa[i] - height[i]

len - sa[i] 為當前後綴的長度,也就是當前後綴所有前綴的個數(字符串從 0 開始)

height[i] 就是相鄰兩後綴 lcp,因為有可能會有相同前綴,而相同前綴在前面已經計算過了

為什麽只需要 height 數組,而不用把任意兩後綴的 lcp 求出來呢?

因為所有後綴已經按照字典序排序了,也就是說,sa[i] 和 sa[i - 1] 的 lcp 即為 sa[i] 和 sa[0 ~ i - 1] 的所有 lcp 的最大值。

——代碼(Tyvj)

技術分享
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #define N 200001
 5 #define LL long long
 6 
 7 LL ans;
 8 int len, m = 256;
 9 int buc[N], x[N], y[N], sa[N], rank[N], height[N];
10 char s[N];
11 
12 inline void build_sa()
13 { 14 int i, k, p; 15 for(i = 0; i < m; i++) buc[i] = 0; 16 for(i = 0; i < len; i++) buc[x[i] = s[i]]++; 17 for(i = 1; i < m; i++) buc[i] += buc[i - 1]; 18 for(i = len - 1; i >= 0; i--) sa[--buc[x[i]]] = i; 19 for(k = 1; k <= len; k <<= 1) 20 { 21
p = 0; 22 for(i = len - 1; i >= len - k; i--) y[p++] = i; 23 for(i = 0; i < len; i++) if(sa[i] >= k) y[p++] = sa[i] - k; 24 for(i = 0; i < m; i++) buc[i] = 0; 25 for(i = 0; i < len; i++) buc[x[y[i]]]++; 26 for(i = 1; i < m; i++) buc[i] += buc[i - 1]; 27 for(i = len - 1; i >= 0; i--) sa[--buc[x[y[i]]]] = y[i]; 28 std::swap(x, y); 29 p = 1, x[sa[0]] = 0; 30 for(i = 1; i < len; i++) 31 x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k] ? p - 1 : p++; 32 if(p >= len) break; 33 m = p; 34 } 35 } 36 37 inline void build_height() 38 { 39 int i, j, k = 0; 40 for(i = 0; i < len; i++) rank[sa[i]] = i; 41 for(i = 0; i < len; i++) 42 { 43 if(!rank[i]) continue; 44 if(k) k--; 45 j = sa[rank[i] - 1]; 46 while(s[i + k] == s[j + k] && i + k < len && j + k < len) k++; 47 height[rank[i]] = k; 48 } 49 } 50 51 int main() 52 { 53 int i; 54 scanf("%d", &len); 55 getchar(); 56 for(i = 0; i < len; i++) 57 { 58 s[i] = getchar(); 59 if((i + 1) % 80 == 0) getchar(); 60 } 61 build_sa(); 62 build_height(); 63 for(i = 0; i < len; i++) ans += (LL)(len - sa[i] - height[i]); 64 printf("%lld\n", ans); 65 return 0; 66 }
View Code

洛谷那題好像數據有點問題。

[TyvjP1515] 子串統計 [luoguP2408] 不同子串個數(後綴數組)