1. 程式人生 > >【轉】HDU 6194 string string string (2017沈陽網賽-後綴數組)

【轉】HDU 6194 string string string (2017沈陽網賽-後綴數組)

char 還要 至少 ace rank using lock long std

轉自:http://blog.csdn.net/aozil_yang/article/details/77929216


題意:

告訴你一個字符串和k , 求這個字符串中有多少不同的子串恰好出現了k 次。

思路:

後綴數組。

我們先考慮至少出現k 次的子串, 所以我們枚舉排好序的後綴i (sa[i]) 。

k段k 段的枚舉。

假設當前枚舉的是 sa[i]~sa[i + k -1]

那麽假設這一段的最長公共前綴 是L 的話。

那麽就有L 個不同的子串至少出現了k次。

我們要減去至少出現k + 1次的 , 但還要和這個k 段的lcp 有關系, 因此肯定就是 這一段 向上找一個後綴 或者向下找一個後綴。

即 sa[i-1] ~ sa[i + k - 1] 和 sa[i] ~ sa[i + k] 求兩次lcp 減去即可。

但是會減多了。

減多的顯然是sa[i-1] ~ sa[i + k] 的lcp。 加上即可。

註意 k =1的情況在求lcp 會有 問題, 即求一個串的最長公共前綴會有問題, 特判一下即可。

一定要註意邊界問題 邊界問題 邊界問題!!!


  1
#include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 100000 + 10; 7 8 int t1[maxn], t2[maxn], c[maxn]; 9 10 bool cmp(int* r, int a,int b,int l){ 11 return r[a] == r[b] && r[a+l] == r[b+l]; 12 } 13 14
void da(int str[], int sa[], int Rank[], int lcp[], int n, int m){ 15 ++n; 16 int i, j, p, *x = t1, *y = t2; 17 for (i = 0; i < m; ++i) c[i] = 0; 18 // puts("hha"); 19 for (i = 0; i < n; ++i) c[x[i] = str[i] ]++; 20 for (i = 1; i < m; ++i) c[i] += c[i-1]; 21 for
(i = n-1; i >= 0; --i) sa[--c[x[i] ] ] = i; 22 for (j = 1; j <= n; j <<= 1){ 23 p = 0; 24 for (i = n-j; i < n; ++i) y[p++] = i; 25 for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j; 26 for (i = 0; i < m; ++i) c[i] = 0; 27 for (i = 0; i < n; ++i) c[x[y[i] ] ]++; 28 29 for (i = 1; i < m; ++i) c[i] += c[i-1]; 30 for (i = n-1; i >= 0; --i) sa[--c[x[y[i] ] ] ] = y[i]; 31 32 swap(x,y); 33 p = 1; x[sa[0] ] = 0; 34 for (i = 1; i < n; ++i){ 35 x[sa[i] ] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++; 36 37 38 } 39 40 if (p >= n)break; 41 m= p; 42 43 44 } 45 46 int k = 0; 47 n--; 48 for (i = 0; i <= n; ++i) Rank[sa[i] ] = i; 49 for (i = 0; i < n; ++i){ 50 if (k)--k; 51 j = sa[Rank[i]-1 ]; 52 while(str[i+k] == str[j+k])++k; 53 lcp[Rank[i] ] = k; 54 } 55 } 56 57 int lcp[maxn], a[maxn], sa[maxn], Rank[maxn]; 58 59 char s[maxn]; 60 61 int d[maxn][40]; 62 int len; 63 64 void rmq_init(int* A, int n){ 65 for (int i = 0; i < n; ++i) d[i][0] = A[i]; 66 for (int j = 1; (1<<j) <= n; ++j) 67 for (int i = 0; i + (1<<j) - 1 < n ; ++i) 68 d[i][j] = min(d[i][j-1], d[i + (1<< (j-1))][j-1]); 69 } 70 71 int ASK(int l,int r){ 72 int k = 0; 73 while((1<<(k+1)) <= r-l + 1)++k; 74 return min(d[l][k], d[r-(1<<k) + 1][k]); 75 } 76 77 int ask(int l,int r){ 78 if (l == r) return len - sa[r]; /// l == r的話 是一個串, 返回本身的長度即可。 79 return ASK(l + 1, r); ///否則在rmq查詢。 80 } 81 82 // 83 int main(){ 84 int T; 85 scanf("%d", &T); 86 87 while(T--){ 88 int k; 89 scanf("%d", &k); 90 scanf("%s", s); 91 len = strlen(s); 92 for (int i = 0; i < len; ++i){ 93 a[i] = s[i] - ‘a‘ + 1; 94 } 95 a[len] = 0; 96 da(a, sa, Rank, lcp, len, 30); 97 rmq_init(lcp, len + 1); 98 long long ans = 0; 99 for (int i = 1; i + k - 1 <= len; ++i){ 100 ans += ask(i, i + k - 1); 101 if (i - 1 > 0)ans -= ask(i - 1, i + k - 1); ///註意邊界問題。 102 if (i + k <= len)ans -= ask(i, i + k); 103 if (i - 1 > 0 && i + k <= len)ans += ask(i - 1 , i + k); 104 } 105 printf("%I64d\n", ans); 106 107 108 } 109 return 0; 110 } 111

【轉】HDU 6194 string string string (2017沈陽網賽-後綴數組)