BZOJ - 3277 - 串
阿新 • • 發佈:2018-12-07
題意
現在給定你n個字串,詢問每個字串有多少子串(不包括空串)是所有n個字串中至少k個字串的子串(注意包括本身)。題解
首先是廣義字尾自動機,就是每次將$last$歸為$Root$,從而將幾個字尾自動機拼在一起處理
那麼現在需要知道每個字串在$n$個母串中的出現次數,所謂字串,就是所有字首的所有後綴,所以可以順著字首走,那麼通過$Parent$樹找字尾,一直往上跳,那麼需要加一的所有後綴就是所屬母串並非當前母串的那幾個
此時再整理出每個所屬母串個數$Size >= K$的初始貢獻,即$Len[i] - Len[Father[i]]$,反之賦$0$
又子串為字首的字尾,那麼一個節點的貢獻即為它祖先至它本身的貢獻字首和,即它所有後綴能夠構成的子串數
最後再遍歷一遍字首統計答案即可
程式碼
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <string> 5 #include <vector> 6 7 using namespace std; 8 9 typedef long long LL; 10 11 const int MAXN = 6e05 + 10; 12 13 intView CodeN, K; 14 string Str[MAXN >> 2]; 15 16 const int Root = 1; 17 int Tree[MAXN][30]= {0}; 18 int Father[MAXN]= {0}; 19 int Len[MAXN]= {0}, Size[MAXN]= {0}; 20 int Belong[MAXN]= {0}; 21 int last = Root, nodes = 1; 22 void Append (int c, int bel) { 23 int fa = last, p = ++ nodes;24 last = p; 25 Len[p] = Len[fa] + 1; 26 while (fa && ! Tree[fa][c]) 27 Tree[fa][c] = p, fa = Father[fa]; 28 if (! fa) 29 Father[p] = 1; 30 else { 31 int x = Tree[fa][c]; 32 if (Len[x] == Len[fa] + 1) 33 Father[p] = x; 34 else { 35 int np = ++ nodes; 36 Len[np] = Len[fa] + 1, Father[np] = Father[x]; 37 Father[p] = Father[x] = np; 38 memcpy (Tree[np], Tree[x], sizeof (Tree[x])); 39 while (fa && Tree[fa][c] == x) 40 Tree[fa][c] = np, fa = Father[fa]; 41 } 42 } 43 } 44 int Topo[MAXN]; 45 int W[MAXN]= {0}; 46 int buck[MAXN]= {0}; 47 void Merge () { 48 for (int i = 1; i <= N; i ++) { 49 int n = Str[i].size(); 50 int p = Root; 51 for (int j = 0; j < n; j ++) { 52 p = Tree[p][Str[i][j] - 'a']; 53 int fa = p; 54 while (fa && Belong[fa] != i) { 55 Size[fa] ++; 56 Belong[fa] = i; 57 fa = Father[fa]; 58 } 59 } 60 } 61 for (int i = 1; i <= nodes; i ++) 62 buck[Len[i]] ++; 63 for (int i = 1; i <= nodes; i ++) 64 buck[i] += buck[i - 1]; 65 for (int i = nodes; i >= 1; i --) 66 Topo[buck[Len[i]] --] = i; 67 for (int i = 1; i <= nodes; i ++) 68 if (Size[i] >= K) 69 W[i] = Len[i] - Len[Father[i]]; 70 for (int i = 1; i <= nodes; i ++) 71 W[Topo[i]] += W[Father[Topo[i]]]; 72 } 73 74 LL Answer[MAXN >> 2]= {0}; 75 76 int main () { 77 scanf ("%d%d", & N, & K); 78 for (int i = 1; i <= N; i ++) { 79 cin >> Str[i]; 80 int n = Str[i].size(); 81 last = Root; 82 for (int j = 0; j < n; j ++) 83 Append (Str[i][j] - 'a', i); 84 } 85 Merge (); 86 for (int i = 1; i <= N; i ++) { 87 int n = Str[i].size(); 88 int p = Root; 89 for (int j = 0; j < n; j ++) 90 p = Tree[p][Str[i][j] - 'a'], Answer[i] += W[p]; 91 } 92 for (int i = 1; i <= N; i ++) { 93 if (i > 1) 94 putchar (' '); 95 printf ("%lld", Answer[i]); 96 } 97 puts (""); 98 99 return 0; 100 } 101 102 /* 103 3 1 104 abc 105 a 106 ab 107 */ 108 109 /* 110 2 2 111 aa 112 aaa 113 */