[关闭]
@lychee123 2017-07-14T04:10:51.000000Z 字数 2310 阅读 1254

ZOJ 3228:Searching the String(AC自动机(两种不同的匹配方式所匹配出的结果))

数据结构


题面链接

先给你一个不超过1000000长度的大串s;接下来输入一个n代表接下来输入的小串个数,小串长度不超过6。
小串分两种类型0和1类型。
0类型表示小串在大串中的最大匹配个数就是常规的AC自动机的做法。
1类型表示小串在大串中不能重合的最大匹配数。
依次输出结果.(所有的串只包含小写字母)
按样例输出,注意每组测试数据后有一个换行。

Sample Input

ab
2
0 ab
1 ab

abababac
2
0 aba
1 aba

abcdefghijklmnopqrstuvwxyz
3
0 abc
1 def
1 jmn
Sample Output

Case 1
1
1

Case 2
3
2

Case 3
1
1
0

当为0类型时就是最基本的AC自动机的解法。
当为1类型时我们就要用数组len[]来记录一下该节点位置单词的长度。用lastpoint[]来记录这个节点位置单词上次出现的位置。在循环大串的时候直接判断 i - len[j] >= lastpoint[j] 是否满足。如果满足说明没有出现覆盖上一次出现的位置,此时这种类型的ans就++。
说明一下。
对于每个小串我们在getfind函数里面都求出0,1两种类型情况下的结果。
用ans[maxn][0],来记录所有小串为0类型时结果。
用ans[maxn][1],来记录所有小串为1类型时结果。
注意!!!
很多小细节应该仔细一点。case的kk已经写错两次了!!!!

对于locate数组。locate数组存的是locate[i]表示第i个小串在树上的结尾节点。由于我们存的小串可能是相同的串,类型不同。如果还是照习惯用isword数组来存他是第几个串,那么相同的串的不同类型会被最后出现的串覆盖掉。所以先出现的小串则会出现找不到的情况。这样是不对的。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn = 6e5 + 6;
  4. int last[maxn], f[maxn];
  5. int ch[maxn][26];
  6. int cnt;
  7. int isword[maxn];//这个点是单词
  8. int locate[maxn];//locate[i]表示第i个单词存在的节点位置
  9. int lastpoint[maxn];//lastpoint[i]表示i这个节点处的单词上一次出现的位置
  10. int len[maxn];//len[i]表示i这个节点处的单词的长度
  11. void insert(char s[], int id)
  12. {
  13. int root = 0;
  14. int l = strlen(s);
  15. for(int i = 0; i < l; i++)
  16. {
  17. if(ch[root][s[i] - 'a'] == 0)
  18. ch[root][s[i] - 'a'] = ++cnt;
  19. root = ch[root][s[i] - 'a'];
  20. }
  21. isword[root] = 1;
  22. len[root] = l;
  23. locate[id] = root;//这个单词处在树上节点的标号
  24. lastpoint[root] = -INT_MAX / 2;//由于单词长度不超过6 所以小于-7都OK
  25. }
  26. void getfail()
  27. {
  28. queue<int>q;
  29. f[0] = 0;
  30. for(int i = 0; i < 26; i++)
  31. {
  32. int u = ch[0][i];
  33. if(u != 0)
  34. {
  35. f[u] = 0;
  36. q.push(u);
  37. last[u] = 0;
  38. }
  39. }
  40. while(!q.empty())
  41. {
  42. int r = q.front();
  43. q.pop();
  44. for(int c = 0; c < 26; c++)
  45. {
  46. int u = ch[r][c];
  47. if(u == 0)
  48. {
  49. ch[r][c] = ch[f[r]][c];
  50. continue;
  51. }
  52. q.push(u);
  53. int v = f[r];
  54. while(v && !ch[v][c])
  55. v = f[v];
  56. f[u] = ch[v][c];
  57. last[u] = isword[f[u]] ? f[u] : last[f[u]];
  58. }
  59. }
  60. }
  61. int ans[maxn][3];
  62. void getfind(char T[])
  63. {
  64. memset(ans, 0, sizeof(ans));
  65. int n = strlen(T);
  66. int j = 0;
  67. for(int i = 0; i < n; i++)
  68. {
  69. j = ch[j][T[i] - 'a'];
  70. if(isword[j])
  71. {
  72. ans[j][0]++;
  73. if(i - len[j] >= lastpoint[j])
  74. {
  75. ans[j][1]++;
  76. lastpoint[j] = i;
  77. }
  78. }
  79. int temp = last[j];
  80. while(temp)
  81. {
  82. ans[temp][0]++;
  83. if(i - len[temp] >= lastpoint[temp])
  84. {
  85. ans[temp][1]++;
  86. lastpoint[temp] = i;
  87. }
  88. temp = last[temp];
  89. }
  90. }
  91. }
  92. char s[maxn];
  93. int type[maxn];
  94. char s1[maxn];
  95. int main()
  96. {
  97. int kk = 0;//这种小细节不要再出问题了!!!!!!!!!
  98. while(~scanf("%s", s))
  99. {
  100. memset(ch, 0, sizeof(ch));
  101. memset(isword, 0, sizeof(isword));
  102. int n;
  103. cnt = 0;
  104. scanf("%d", &n);
  105. for(int i = 1; i <= n; i++)
  106. {
  107. scanf("%d%s", &type[i], s1);
  108. insert(s1, i);
  109. }
  110. getfail();
  111. getfind(s);
  112. printf("Case %d\n", ++kk);
  113. for(int i = 1; i <= n; i++)
  114. {
  115. printf("%d\n", ans[locate[i]][type[i]]);
  116. }
  117. puts("");
  118. }
  119. return 0;
  120. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注