AC自动机(HDU 2222: Keywords Search)



题意:

输入n个单词,再输入一篇文章,判断有多少个单词在文章中出现过

http://blog.csdn.net/niushuai666/article/details/7002823

注释都在代码里

#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
struct Trie
{
	int ans, len, x, now, root, i, loc, temp, next[500005][26], fail[500005], sum[500005];
	int Newnode()		/*申请新的节点,这个节点的位置是loc*/
	{
		for(i=0;i<=25;i++)		/*初始化当前节点*/
			next[loc][i] = -1;
		sum[loc++] = 0;				/*节点申请完毕,当前位置loc已被占用,所以让loc+1以便下次申请*/
		return loc-1;		/*返回当前节点位置*/
	}
	void Init()		/*初始化字典树*/
	{
		loc = 0;
		root = Newnode();
	}
	void Update(char a[])		/*字典树的更新*/
	{
		int len = strlen(a);
		int now = root;
		for(int i=0;i<len;i++)
		{
			x = a[i]-'a';
			if(next[now][x]==-1)			/*如果下一个字符节点不存在,建立新节点*/
				next[now][x] = Newnode();
			now = next[now][x];
		}
		sum[now]++;		/*sum[k]表示k节点是sum[k]个单词的结尾*/
	}
	void Create()
	{
		queue<int> q;				/*fail[k]的作用:文章如果在k节点匹配失败,则可以跳到fail[k]处继续匹配,这个同理KMP的next[]数组*/
		fail[root] = root;
		for(i=0;i<=25;i++)
		{
			if(next[root][i]==-1)
				next[root][i] = root;
			else
			{
				fail[next[root][i]] = root;				/*初始化根节点与它的所有子节点的失败指针*/
				q.push(next[root][i]);			/*根节点与它的所有子节点(位置loc)进入队列*/
			}
		}
		while(q.empty()==0)			/*从字典树的根开始进行广搜*/
		{
			now = q.front();
			q.pop();
			for(i=0;i<=25;i++)
			{
				if(next[now][i]==-1)				/*k节点的某个子节点不存在,那么可以跳到fail[k]对应的子节点*/
					next[now][i] = next[fail[now]][i];
				else
				{
					fail[next[now][i]] = next[fail[now]][i];	/*k节点的某个子节点p存在,那么fail[p]就是fail[k]对应相同字符的子节点*/
					q.push(next[now][i]);
				}
			}
		}
	}
	int Query(char a[])
	{
		ans = 0;
		len = strlen(a);
		now = root;
		for(i=0;i<len;i++)
		{
			now = next[now][a[i]-'a'];
			temp = now;
			while(temp!=root)			/*可能多个节点满足匹配条件,即某个单词后缀为另一个单词的前缀(awigknm的m所在节点可以跳到gknm的m所在节点)*/
			{												/*↑↑↑KMP的套路↑↑↑*/
				ans += sum[temp];
				sum[temp] = 0;					/* 文章中出现同样的单词只会被算作一次*/
				temp = fail[temp];
			}
		}
		return ans;
	}
};
char str[1000005];
Trie AC;
int main(void)
{
	int T, i, n;
	scanf("%d", &T);
	while(T--)
	{
		AC.Init();
		scanf("%d", &n);
		for(i=1;i<=n;i++)
		{
			scanf("%s", str);
			AC.Update(str);
		}
		AC.Create();
		scanf("%s", str);
		printf("%d\n", AC.Query(str));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值