初识AC自动机 HDU2222

来源:互联网 发布:二维数组回形遍历 编辑:程序博客网 时间:2024/05/29 19:07

做了一天了,看懂了AC自动机的原理,纯属脑洞码了一天~

几个小函数解释如下:

  • insert即trie树建立的过程
  • KMP是找到trie树中的每个节点的后缀节点,和一维的字符串差不多
  • find(x,d)是找到x结点下经过字符d转化到的下一个结点,可能会出现没有的情况,标记为0,即重新返回根节点

HDU2222

注意点:

  • 若关键字A与关键字B重复,在主串中出现一个算两次
  • 若关键字A在主串种出现两次,则只算一次
  • 在主串经过的每个结点,都访问该节点的后缀节点,看其是否是终止结点。
  • 访问过的一系列后缀节点可以做标记,下次访问无需再重新累加。
#include <cstdio>#include <iostream>#include <cstring>#include <queue>#include <algorithm>using namespace std;int ans, size;struct node{char ch;int next[26], fa, end, pre, have;void pr(){printf("%c %d %d %d\n", ch, fa, pre, end);}}tree[250005];char str[10001][51];void insert(char *key){char *p = key;int s = 0;while(*p){if(!tree[s].next[*p-'a'])tree[s].next[*p-'a'] = ++size;int t = s;s = tree[s].next[*p-'a'];tree[s].fa = t;tree[s].ch = *p;p++;}tree[s].end++;//repeated key words can be calculated more than once}int find(int x, char d){return tree[x].next[d-'a'];}void KMP(char *key){char *p = key;int s = find(0, *p);tree[s].pre = 0;p++;while(*p){int i = s;s = find(s, *p);//printf("%d %c\n", s, *p);int j = tree[i].pre;while(j > 0 && !find(j, *p))j = tree[j].pre;tree[s].pre = find(j, *p);p++;}}int sig(int x)//visit x, x.pre, x.pre.pre, ....{//printf("sig tree[%d]  have %d  pre %d  end %d\n", //x, tree[x].have, tree[x].pre, tree[x].end);if(!x)return 0;if(tree[x].have)return 0;tree[x].have = 1;return tree[x].end + sig(tree[x].pre);}int main(){int T, n;scanf("%d", &T);while(T--){memset(tree, 0, sizeof(tree));ans = size = 0;scanf("%d", &n);for(int i = 0;i < n;i++){scanf("%s", str[i]);insert(str[i]);}tree[0].pre = 0;for(int i = 0;i < n;i++)KMP(str[i]);/*for(int i = 0;i <= size;i++){printf("tree[%d]  ", i);tree[i].pr();}*/char c;int s = 0;getchar();while((c = getchar()) != '\n'){//printf("this  %c  ", c);while(s > 0 && !tree[s].next[c-'a']){s = tree[s].pre;ans += sig(s);}if(tree[s].next[c-'a']){ans += sig(s);s = tree[s].next[c-'a'];}ans += sig(s);//printf("s  %d   ans %d\n", s, ans);}printf("%d\n", ans);}return 0;}


0 0