[关闭]
@ljt12138 2017-04-16T23:59:57.000000Z 字数 13622 阅读 825

诸神眷顾的幻想乡

给定一个叶节点不超过20的无根树,每个节点有一个字母。问树上路径形成的本质不同的字符串的个数。

广义后缀自动机裸题。从每个叶节点做bfs,记录父亲的状态从而插入建立后缀自动机。我们知道一个后缀自动机本质不同的子串个数为 imax[i]min[i]+1=max[i]max[fa[i]],或者DAG上dp即可。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 100001*20, S = 10;
  4. struct SAM {
  5. int chl[MAXN][S], fa[MAXN], maxl[MAXN];
  6. int top, root, last;
  7. void init()
  8. {
  9. top = root = last = 1;
  10. memset(chl, 0, sizeof chl);
  11. memset(fa, 0, sizeof fa);
  12. memset(maxl, 0, sizeof maxl);
  13. }
  14. void push(int stat, int x)
  15. {
  16. int p = stat, np = ++top; maxl[np] = maxl[p] + 1;
  17. while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
  18. if (!p) fa[np] = root;
  19. else {
  20. int q = chl[p][x];
  21. if (maxl[q] == maxl[p] + 1) fa[np] = q;
  22. else {
  23. int nq = ++top; maxl[nq] = maxl[p] + 1;
  24. memcpy(chl[nq], chl[q], sizeof chl[q]);
  25. fa[nq] = fa[q], fa[q] = fa[np] = nq;
  26. while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
  27. }
  28. }
  29. last = np;
  30. }
  31. }sam;
  32. queue<int> que;
  33. int stat[102400], rd[102400], col[102400];
  34. struct node {
  35. int to, next;
  36. } edge[202400];
  37. int head[102400], top = 0;
  38. void push(int i, int j)
  39. { rd[i]++, ++top, edge[top] = (node) {j, head[i]}, head[i] = top; }
  40. void bfs(int nd)
  41. {
  42. //printf("BFS : %d\n", nd);
  43. memset(stat, 0, sizeof stat);
  44. stat[nd] = 1;
  45. que.push(nd);
  46. while (!que.empty()) {
  47. int tp = que.front(); que.pop();
  48. sam.push(stat[tp], col[tp]);
  49. //printf("%d -- %d--+%d-->%d\n", tp, stat[tp], col[tp], sam.last);
  50. for (int i = head[tp]; i; i = edge[i].next)
  51. if (!stat[edge[i].to])
  52. stat[edge[i].to] = sam.last, que.push(edge[i].to);
  53. }
  54. }
  55. int n, c;
  56. void solve()
  57. {
  58. sam.init();
  59. scanf("%d%d", &n, &c);
  60. for (int i = 1; i <= n; i++)
  61. scanf("%d", &col[i]);
  62. for (int i = 1; i < n; i++) {
  63. int u, v; scanf("%d%d", &u, &v);
  64. push(u, v); push(v, u);
  65. }
  66. for (int i = 1; i <= n; i++)
  67. if (rd[i] == 1)
  68. bfs(i);
  69. long long ans = 0;
  70. for (int i = 2; i <= sam.top; i++)
  71. ans += sam.maxl[i] - sam.maxl[sam.fa[i]];
  72. printf("%lld", ans);
  73. }
  74. int main()
  75. {
  76. //freopen("zjoi15_substring.in", "r", stdin);
  77. //freopen("zjoi15_substring.out", "w", stdout);
  78. solve();
  79. return 0;
  80. }

POI2000 公共串

给你n5个长度不超过2000的字符串,求最长公共子串。

SAM解法:只要在匹配的时候记录每个节点对于第i个串匹配的最长距离,然后xjb取max和min就好了。

APIO2014 回文串

给定一个字符串S,求一个回文子串T,最大化 |T|times(T)times(T) 为出现次数。

首先我们用manacher算法求出本质不同的回文串。由于manacher的复杂度为O(n),回文串个数为O(n)。我们一个个询问其出现次数即可。现在问题转化为了对于一个子串,要在 O(lgn) 的时间内求出其出现次数。

首先我们预处理出pos[r]:=S[1..r]在后缀自动机中的位置,那么 S[l,r] 就是parent树上最后一个maxl大于l-r+1的节点。用倍增处理即可。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 300005*2, S = 26;
  4. int pos[MAXN]; // S[1..r]对应状态
  5. int fa[MAXN][21];
  6. char str[MAXN];
  7. int right_siz[MAXN];
  8. int stk[MAXN], top = 0, rd[MAXN];
  9. struct SAM {
  10. int chl[MAXN][S], fa[MAXN], maxl[MAXN];
  11. int top, root, last;
  12. void clear()
  13. {
  14. top = root = last = 1;
  15. memset(chl, 0, sizeof chl), memset(fa, 0, sizeof fa), memset(maxl, 0, sizeof maxl);
  16. }
  17. SAM() { clear(); }
  18. void push(int x)
  19. {
  20. int p = last, np = ++top; maxl[np] = maxl[p] + 1, right_siz[np]++;
  21. while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
  22. if (!p) fa[np] = root;
  23. else {
  24. int q = chl[p][x];
  25. if (maxl[q] == maxl[p] + 1) fa[np] = q;
  26. else {
  27. int nq = ++top; maxl[nq] = maxl[p]+1;
  28. memcpy(chl[nq], chl[q], sizeof chl[q]);
  29. fa[nq] = fa[q], fa[q] = fa[np] = nq;
  30. while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
  31. }
  32. }
  33. last = np;
  34. }
  35. } sam;
  36. void top_sort()
  37. {
  38. for (int i = 1; i <= sam.top; i++) rd[sam.fa[i]]++;
  39. for (int i = 1; i <= sam.top; i++) if (rd[i] == 0) stk[++top] = i;
  40. while (top) {
  41. int t = stk[top--]; rd[sam.fa[t]]--, right_siz[sam.fa[t]] += right_siz[t];
  42. if (rd[sam.fa[t]] == 0) stk[++top] = sam.fa[t];
  43. }
  44. }
  45. void init()
  46. {
  47. scanf("%s", str+1);
  48. for (char *p = str+1; *p != '\0'; ++p)
  49. sam.push(*p-'a');
  50. int len = strlen(str+1);
  51. for (int i = 1, nd = sam.root; i <= len; i++) {
  52. nd = sam.chl[nd][str[i]-'a'];
  53. pos[i] = nd;
  54. }
  55. for (int i = 1; i <= sam.top; i++) fa[i][0] = sam.fa[i];
  56. for (int j = 1; j <= 20; j++)
  57. for (int i = 1; i <= sam.top; i++)
  58. fa[i][j] = fa[fa[i][j-1]][j-1];
  59. top_sort(); // Count right_siz
  60. }
  61. long long ans = 0;
  62. void query(int i, int j)
  63. {
  64. int nd = pos[j];
  65. for (int k = 20; k >= 0; k--)
  66. if (sam.maxl[fa[nd][k]] >= j-i+1)
  67. nd = fa[nd][k];
  68. ans = max(ans, (long long)(j-i+1)*right_siz[nd]);
  69. }
  70. int p[MAXN];
  71. void work()
  72. {
  73. int len = strlen(str+1);
  74. int id = 0, mx = 0; // manacher
  75. str[0] = '$';
  76. for (int i = 1; i <= len; i++) {
  77. if (mx > i) p[i] = min(p[id-(i-id)], mx-i); else p[i] = 1, query(i, i);
  78. while (str[i-p[i]] == str[i+p[i]]) query(i-p[i], i+p[i]), p[i]++;
  79. if (i+p[i] > mx) id = i, mx = i+p[i];
  80. }
  81. id = mx = 0;
  82. for (int i = 1; i <= len; i++) {
  83. if (mx > i) p[i] = min(p[id-(i-id)], mx-i); else p[i] = 0;
  84. while (str[i-p[i]] == str[i+p[i]+1]) query(i-p[i], i+p[i]+1), p[i]++;
  85. if (i+p[i] > mx) id = i, mx = i+p[i];
  86. }
  87. cout << ans << endl;
  88. }
  89. int main()
  90. {
  91. init();
  92. work();
  93. return 0;
  94. }

HAOI2016找相同字符

给定两个串S1,S2,统计他们的公共子串总数。两个子串不同,当且仅当长度不同或出现位置不同。

SA解法:将S1和S2用一个'#'隔开,求出height数组,由于公共子串是后缀的前缀,因此答案就是所有前一半的后缀和后一半的后缀的lcp的和。用单调栈扫两遍记录答案即可。最优复杂度O(n)

SAM解法:这个做法比较鬼畜。先把第一个串建立后缀自动机,再把第二个串在上面跑。到达一个状态x时匹配长度为len对答案的贡献分为两部分:

  1. 当前位置的收获 (lenmin(x)+1)×|Right(x)|
  2. 由于匹配了当前位置,自然匹配了父亲节点,就有 ix(max(i)min(i)+1)×|Right(i)|

第一部分为O(1),第二部分可以拓扑排序后 O(n) 预处理。总复杂度为O(n)

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 200005*2, S = 26;
  4. int right_siz[MAXN];
  5. struct SAM {
  6. int chl[MAXN][S], fa[MAXN], maxl[MAXN];
  7. int top, root, last;
  8. void clear()
  9. { top = root = last = 1; }
  10. SAM()
  11. { clear(); }
  12. void push(int x)
  13. {
  14. int p = last, np = ++top; maxl[np] = maxl[p] + 1; right_siz[np]++;
  15. while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
  16. if (!p) fa[np] = root;
  17. else {
  18. int q = chl[p][x];
  19. if (maxl[q] == maxl[p] + 1) fa[np] = q;
  20. else {
  21. int nq = ++top; maxl[nq] = maxl[p] + 1;
  22. memcpy(chl[nq], chl[q], sizeof chl[q]);
  23. fa[nq] = fa[q], fa[q] = fa[np] = nq;
  24. while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
  25. }
  26. }
  27. last = np;
  28. }
  29. } sam;
  30. char s1[MAXN], s2[MAXN];
  31. int stk[MAXN], top = 0;
  32. int rd[MAXN];
  33. int topo[MAXN], tp_top = 0;
  34. int canc[MAXN];
  35. int vis[MAXN];
  36. void dfs(int nd, string str)
  37. {
  38. printf("Id = %d, pre = %d, dis = %d, right = %d, cacc = %d\n", nd, sam.fa[nd], sam.maxl[nd], right_siz[nd], canc[nd]);
  39. vis[nd] = 1;
  40. for (int i = 0; i < S; i++)
  41. if (sam.chl[nd][i])
  42. printf("-+%c-> %d\n", i+'a', sam.chl[nd][i]);
  43. for (int i = 0; i < S; i++)
  44. if (sam.chl[nd][i] && !vis[sam.chl[nd][i]])
  45. dfs(sam.chl[nd][i], str+char(i+'a'));
  46. }
  47. void top_sort()
  48. {
  49. for (int i = 1; i <= sam.top; i++) rd[sam.fa[i]]++;
  50. for (int i = 1; i <= sam.top; i++) if (rd[i] == 0) stk[++top] = i;
  51. while (top) {
  52. int t = stk[top--]; topo[++tp_top] = t, rd[sam.fa[t]]--;
  53. if (rd[sam.fa[t]] == 0) stk[++top] = sam.fa[t];
  54. }
  55. for (int i = 1; i <= tp_top; i++) right_siz[sam.fa[topo[i]]] += right_siz[topo[i]];
  56. for (int i = tp_top; i >= 1; i--)
  57. if (topo[i] != sam.root && sam.fa[topo[i]] != sam.root)
  58. canc[topo[i]] = canc[sam.fa[topo[i]]] + (sam.maxl[sam.fa[topo[i]]]-sam.maxl[sam.fa[sam.fa[topo[i]]]])*right_siz[sam.fa[topo[i]]];
  59. }
  60. void work()
  61. {
  62. scanf("%s%s", s1, s2);
  63. for (char *p = s1; *p != '\0'; p++) sam.push(*p-'a');
  64. top_sort();
  65. int nd = sam.root, len = 0;
  66. long long ans = 0;
  67. //int l = strlen(s2); s2[l] = '$', s2[l+1] = '\0';
  68. for (char *p = s2; *p != '\0'; p++) {
  69. if (sam.chl[nd][*p-'a']) nd = sam.chl[nd][*p-'a'], len++;
  70. else {
  71. while (nd && !sam.chl[nd][*p-'a']) nd = sam.fa[nd];
  72. if (!nd) nd = sam.root, len = 0;
  73. else len = sam.maxl[nd]+1, nd = sam.chl[nd][*p-'a'];
  74. }
  75. ans += canc[nd] + (len-sam.maxl[sam.fa[nd]])*right_siz[nd];
  76. //cout << ans << endl;
  77. }
  78. cout << ans << endl;
  79. }
  80. int main()
  81. {
  82. work();
  83. return 0;
  84. }

2555: SubString

LCT维护Right数组大小...

神题。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 1600002, S = 26;
  4. struct LCT {
  5. int chl[MAXN][2], fa[MAXN], siz[MAXN], flag[MAXN], rev[MAXN], add[MAXN];
  6. int stk[MAXN];
  7. int root, top;
  8. void clear()
  9. { root = top = 0; }
  10. LCT()
  11. { clear(); }
  12. bool isrt(int nd)
  13. { return chl[fa[nd]][0] != nd && chl[fa[nd]][1] != nd; }
  14. void pdw(int nd)
  15. {
  16. int &lc = chl[nd][0], &rc = chl[nd][1];
  17. if (lc) rev[lc] ^= rev[nd], add[lc] += add[nd];
  18. if (rc) rev[rc] ^= rev[nd], add[rc] += add[nd];
  19. if (rev[nd]) rev[nd] = 0, swap(lc, rc);
  20. if (add[nd]) siz[nd] += add[nd], add[nd] = 0;
  21. }
  22. void zig(int nd)
  23. {
  24. int p = fa[nd], g = fa[p];
  25. int tp = chl[p][0] != nd, tg = chl[g][0] != p, son = chl[nd][tp^1];
  26. if (!isrt(p)) chl[g][tg] = nd;
  27. chl[nd][tp^1] = p, chl[p][tp] = son;
  28. fa[nd] = g, fa[p] = nd, fa[son] = p;
  29. }
  30. void splay(int nd)
  31. {
  32. int top = 0; stk[++top] = nd;
  33. for (int x = nd; !isrt(x); x = fa[x])
  34. stk[++top] = fa[x];
  35. while (top) pdw(stk[top--]);
  36. while (!isrt(nd)) {
  37. int p = fa[nd], g = fa[p];
  38. int tp = chl[p][0] != nd, tg = chl[g][0] != p;
  39. if (isrt(p)) { zig(nd); break; }
  40. else if (tp == tg) zig(p), zig(nd);
  41. else zig(nd), zig(nd);
  42. }
  43. }
  44. void dfs(int nd, int tab)
  45. {
  46. if (!nd) return;
  47. for (int i = 1; i <= tab; i++) putchar(' ');
  48. printf("nd = %d, flag = %d, siz = %d, lc = %d, rc = %d, fa = %d, rev = %d\n", nd, flag[nd], siz[nd], chl[nd][0], chl[nd][1], fa[nd], rev[nd]);
  49. dfs(chl[nd][0], tab+2);
  50. dfs(chl[nd][1], tab+2);
  51. }
  52. void access(int x)
  53. {
  54. for (int y = 0; x; x = fa[y = x])
  55. splay(x), chl[x][1] = y;
  56. }
  57. void mkt(int x)
  58. { access(x), splay(x), rev[x] ^= 1; }
  59. void link(int x, int y)
  60. { mkt(x); splay(x); fa[x] = y; }
  61. void cut(int x, int y)
  62. { mkt(x), access(y), splay(y), fa[x] = chl[y][0] = 0;}
  63. void lct_link(int x, int y) // x->y
  64. {
  65. //printf("LINK : %d-->%d\n", x, y);
  66. link(x, y), mkt(1);
  67. //puts("---");
  68. access(y), splay(y), siz[y] += siz[x];
  69. //puts("---");
  70. if (chl[y][0]) add[chl[y][0]] += siz[x];
  71. }
  72. void lct_cut(int x, int y) // cut x->y
  73. {
  74. cut(x, y), mkt(1);
  75. access(y), splay(y), siz[y] -= siz[x];
  76. if (chl[y][0]) add[chl[y][0]] -= siz[x];
  77. }
  78. void set_flag(int x)
  79. { mkt(x), splay(x), siz[x] = 1; }
  80. int find_fa(int x)
  81. {
  82. access(x);
  83. while (!isrt(x)) x = fa[x];
  84. return x;
  85. }
  86. int query(int nd)
  87. {
  88. mkt(nd), splay(nd);
  89. return siz[nd];
  90. }
  91. } lct;
  92. struct SAM {
  93. int chl[MAXN*2][S], fa[MAXN*2], maxl[MAXN*2];
  94. int top, last, root;
  95. void clear()
  96. { top = last = root = 1; }
  97. SAM()
  98. { clear(); }
  99. void push(int x)
  100. {
  101. //cout << "PUSH : " << (char)(x+'a') << endl;
  102. int p = last, np = ++top; maxl[np] = maxl[p] + 1; lct.set_flag(np);
  103. //puts("j");
  104. while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
  105. //puts("jj");
  106. if (!p) fa[np] = root, lct.lct_link(np, root);
  107. else {
  108. int q = chl[p][x];
  109. if (maxl[q] == maxl[p] + 1) fa[np] = q, lct.lct_link(np, q);
  110. else {
  111. int nq = ++top; maxl[nq] = maxl[p] + 1;
  112. memcpy(chl[nq], chl[q], sizeof chl[q]);
  113. lct.lct_link(nq, fa[q]), fa[nq] = fa[q];
  114. lct.lct_cut(q, fa[q]), lct.lct_link(q, nq), fa[q] = nq;
  115. lct.lct_link(np, nq), fa[np] = nq;
  116. while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
  117. }
  118. }
  119. //puts("jjj");
  120. last = np;
  121. }
  122. } sam;
  123. char str[MAXN*2];
  124. int q, mask = 0;
  125. void decode(int mask)
  126. {
  127. int len = strlen(str);
  128. for (int j = 0; j < len; j++) {
  129. mask = (mask*131+j)%len;
  130. swap(str[j], str[mask]);
  131. }
  132. }
  133. void get_str(char str[])
  134. {
  135. scanf("%s", str);
  136. decode(mask);
  137. }
  138. char opt[10];
  139. int main()
  140. {
  141. //freopen("substring.in", "r", stdin);
  142. //freopen("substring.out","w",stdout);
  143. scanf("%d", &q);
  144. scanf("%s", str);
  145. //puts("hah");
  146. for (char *p = str; *p != '\0'; p++)
  147. sam.push(*p-'A');
  148. //puts("hah");
  149. for (int i = 1; i <= q; i++) {
  150. scanf("%s", opt);
  151. //cout << opt << endl;
  152. if (opt[0] == 'A') {
  153. get_str(str);
  154. for (char *p = str; *p != '\0'; p++)
  155. sam.push(*p-'A');
  156. } else {
  157. get_str(str);
  158. int nd = sam.root, flag = 0;
  159. for (char *p = str; *p != '\0'; p++) {
  160. if (!sam.chl[nd][*p-'A']) {flag = 1; break; }
  161. else nd = sam.chl[nd][*p-'A'];
  162. }
  163. if (flag) puts("0");
  164. else {
  165. int ans = lct.query(nd);
  166. printf("%d\n", ans);
  167. mask ^= ans;
  168. }
  169. }
  170. }
  171. return 0;
  172. }

poj2774: Long Long Message

裸题,SAM直接碾。

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. using namespace std;
  5. const int MAXN = 200005;
  6. int chl[MAXN][26], fa[MAXN], maxl[MAXN];
  7. int top = 1, root = 1, last = 1;
  8. void push(int x)
  9. {
  10. int p = last, np = ++top; maxl[np] = maxl[p]+1;
  11. while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
  12. if (!p) fa[np] = root;
  13. else {
  14. int q = chl[p][x];
  15. if (maxl[q] == maxl[p]+1) fa[np] = q;
  16. else {
  17. int nq = ++top; maxl[nq] = maxl[p]+1;
  18. memcpy(chl[nq], chl[q], sizeof chl[q]);
  19. fa[nq] = fa[q], fa[q] = fa[np] = nq;
  20. while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
  21. }
  22. }
  23. last = np;
  24. }
  25. char str[MAXN];
  26. int main()
  27. {
  28. scanf("%s", str);
  29. for (char *p = str; *p != '\0'; ++p)
  30. push(*p-'a');
  31. scanf("%s", str);
  32. int nd = root, len = 0, ans = 0;
  33. for (char *p = str; *p != '\0'; ++p) {
  34. int x = *p-'a';
  35. if (chl[nd][x]) nd = chl[nd][x], len++;
  36. else {
  37. while (nd && !chl[nd][x]) nd = fa[nd];
  38. if (!nd) nd = root, len = 0;
  39. else len = maxl[nd]+1, nd = chl[nd][x];
  40. }
  41. ans = max(ans, len);
  42. }
  43. cout << ans << endl;
  44. return 0;
  45. }

[SPOJ705]不同的子串

模板复习计划,裸题。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 50005*2;
  4. struct SAM {
  5. int chl[MAXN][26], fa[MAXN], maxl[MAXN];
  6. int top, root, last;
  7. void clear()
  8. { top = root = last = 1, memset(chl, 0, sizeof chl),
  9. memset(fa, 0, sizeof fa), memset(maxl, 0, sizeof maxl); }
  10. SAM() { clear(); }
  11. void push(int stat, int x)
  12. {
  13. int p = last, np = ++top; maxl[np] = maxl[p]+1;
  14. while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
  15. if (!p) fa[np] = root;
  16. else {
  17. int q = chl[p][x];
  18. if (maxl[q] == maxl[p]+1) fa[np] = q;
  19. else {
  20. int nq = ++top; maxl[nq] = maxl[p]+1;
  21. memcpy(chl[nq], chl[q], sizeof chl[q]);
  22. fa[nq] = fa[q], fa[q] = nq, fa[np] = nq;
  23. while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
  24. }
  25. }
  26. last = np;
  27. }
  28. } sam;
  29. char str[MAXN];
  30. int main()
  31. {
  32. freopen("subst1.in", "r", stdin);
  33. freopen("subst1.out", "w", stdout);
  34. scanf("%s", str+1);
  35. for (char *p = str+1; *p != '\0'; ++p)
  36. sam.push(sam.last, *p-'A');
  37. long long ans = 0;
  38. for (int i = 1; i <= sam.top; i++)
  39. ans += sam.maxl[i]-sam.maxl[sam.fa[i]];
  40. cout << ans << endl;
  41. return 0;
  42. }

bzoj2806: [Ctsc2012]Cheat

比较神的题...
后缀自动机上dp,由于dp决策有区间性质,可以用线段树或者单调队列维护。线段树版本(O(nlg2n))会TLE,明天写单调队列的。

线段树(TLE):

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN = 1100001*2;
  4. int tree[MAXN][2], fin[MAXN], col[MAXN], trie_root = 0, trie_top = 0;
  5. void push_str(int &nd, const char *str)
  6. {
  7. if (!nd) nd = ++trie_top;
  8. if (*str == '\0') fin[nd] = 1;
  9. else push_str(tree[nd][*str-'0'], str+1), col[tree[nd][*str-'0']] = *str-'0';
  10. }
  11. int chl[MAXN][2], fa[MAXN], maxl[MAXN], root = 1, top = 1;
  12. void push(int p, int x, int &last)
  13. {
  14. int np = ++top; maxl[np] = maxl[p]+1;
  15. while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
  16. if (!p) fa[np] = root;
  17. else {
  18. int q = chl[p][x];
  19. if (maxl[q] == maxl[p]+1) fa[np] = q;
  20. else {
  21. int nq = ++top; maxl[nq] = maxl[p]+1;
  22. chl[nq][0] = chl[q][0], chl[nq][1] = chl[q][1];
  23. fa[nq] = fa[q], fa[q] = fa[np] = nq;
  24. while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
  25. }
  26. }
  27. last = np;
  28. }
  29. queue<int> que;
  30. int stat[MAXN];
  31. void build_tree()
  32. {
  33. que.push(trie_root), stat[trie_root] = 1;
  34. while (!que.empty()) {
  35. int tp = que.front(); que.pop();
  36. int last;
  37. push(stat[tp], col[tp], last);
  38. for (int i = 0; i <= 1; i++)
  39. if (tree[tp][i])
  40. stat[tree[tp][i]] = last, que.push(tree[tp][i]);
  41. }
  42. }
  43. bool match(char str[], int l, int r)
  44. {
  45. if (l <= 0 || l > r) return 0;
  46. int nd = root, ans = 0, len = 0;
  47. for (int i = l; i <= r; i++) {
  48. int x = str[i]-'0';
  49. if (chl[nd][x]) nd = chl[nd][x], len++;
  50. else {
  51. while (nd && !chl[nd][x]) nd = fa[nd];
  52. if (!nd) nd = root, len = 0;
  53. else len = maxl[nd]+1, nd = chl[nd][x];
  54. }
  55. ans = max(ans, len);
  56. }
  57. return ans == r-l+1;
  58. }
  59. char str[MAXN];
  60. int dp[MAXN], max_back[MAXN];
  61. int n, m;
  62. int zkw[(1<<21)+1], N = 1<<20;
  63. void modify(int nd, int val)
  64. {
  65. nd += N-1;
  66. zkw[nd] = val;
  67. for (int i = nd>>1; i; i >>= 1)
  68. zkw[i] = max(zkw[i*2], zkw[i*2+1]);
  69. }
  70. int ask_max(int l, int r)
  71. {
  72. if (l > r) return 0;
  73. int ans = -233333333;
  74. for (l += N-1, r += N-1; l < r; l>>=1, r>>=1) {
  75. if (l&1) ans = max(ans, zkw[l++]);
  76. if (!(r&1)) ans = max(ans, zkw[r--]);
  77. }
  78. if (l == r) ans = max(ans, zkw[l]);
  79. return ans;
  80. }
  81. bool do_dp(int L)
  82. {
  83. int l = strlen(str+1);
  84. memset(dp, 0, sizeof dp);
  85. memset(zkw, -127/3, sizeof zkw);
  86. int max_val = 0;
  87. modify(0, 0);
  88. for (int i = 1; i <= l; i++) {
  89. dp[i] = max_val;
  90. if (i-L >= 0 && max_back[i] <= i-L)
  91. dp[i] = max(dp[i], i+ask_max(max_back[i], i-L));
  92. max_val = max(max_val, dp[i]);
  93. modify(i, dp[i]-i);
  94. }
  95. return dp[l]*10 >= l*9;
  96. }
  97. int main()
  98. {
  99. scanf("%d%d", &n, &m);
  100. for (int i = 1; i <= m; i++) {
  101. scanf("%s", str);
  102. push_str(trie_root, str);
  103. }
  104. build_tree();
  105. for (int i = 1; i <= n; i++) {
  106. scanf("%s", str+1);
  107. memset(max_back, 0, sizeof max_back);
  108. for (int i = 1, l = strlen(str+1); i <= l; i++) {
  109. int lf = 1, rt = i, mid;
  110. while (lf <= rt) {
  111. mid = (lf+rt)>>1;
  112. if (match(str, i-mid+1, i)) lf = mid+1;
  113. else rt = mid-1;
  114. }
  115. max_back[i] = i-(lf-1);
  116. }
  117. int l = 1, r = strlen(str+1), mid;
  118. while (l <= r) {
  119. mid = (l+r)>>1;
  120. if (do_dp(mid)) l = mid+1;
  121. else r = mid-1;
  122. }
  123. printf("%d\n", l-1);
  124. }
  125. return 0;
  126. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注