初识AC自动机 HDU2222

做了一天了,看懂了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;
}


你可能感兴趣的:(算法,AC自动机)