hdu2222


   初学AC自动机,在网上查了半天资料,没看懂,最后竟还是看了一篇别人推荐的英语原文,才渐渐懂了。学习AC自动机,首先我们要有字典树和KMP的基础,字典树作为数据存储的结构基础,而KMP则为算法基础(KMP可理解为特殊的AC自动机,此时所建的字典树为一叉树)。

算法思想懂了后,就开始做这道网友极力推荐的AC自动机模板题了,哎,虽然说思想懂了,但真真实现起来也花费了差不多一天的时间委屈。。无论如何,这都是自己做的第一道AC自动机的题目,还是勉励一下自己吧奋斗

以下是讲解AC自动机的一篇比较好的文章,虽然是英文的,但真的写得很好,强烈建议看一下 http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1000005;
struct node
{
	int count;
	node *next[26];
	node *fail;
}memory[maxn],*root;
int cur;
node *q[maxn];

node * build_node()//添加新的节点
{
	node *p=&memory[cur];
	p->count=0;
	for(int i=0;i<26;i++)
		p->next[i]=NULL;
	cur++;
	return p;
}

void insert_tree(char *s)//添加单词到字典树中
{
	int len,index;
	node *p=root;
	len=strlen(s);
	for(int i=0;i<len;i++)
	{
		index=s[i]-'a';
		if(p->next[index]==NULL)
			p->next[index]=build_node();
		p=p->next[index];
	}
	p->count++;
}

void BFS()//建失败指针
{
	node *p,*u,*v;
	int f,r;
	root->fail=root;
	f=r=0;
	for(int i=0;i<26;i++)//先将第一层节点入栈,fail指针指向root
		if(root->next[i]!=NULL)
		{
			q[r++]=root->next[i];
			root->next[i]->fail=root;
		}
	while(f!=r)
	{
		p=q[f++];
		for(int i=0;i<26;i++)
		{
			u=p->next[i];
			if(u!=NULL)//p->u有边
			{
				q[r++]=u;//入栈
				v=p->fail;
				while(v!=root&&v->next[i]==NULL)//找最长后缀
					v=v->fail;
				u->fail=(v->next[i]==NULL)?root:v->next[i];//判断一下最长后缀是否存在
			}
		}
	}
}

int AC_AUTO(char *s)
{
	int len,ans,index;
	node *p,*tmp;
	len=strlen(s);
	p=root;
	ans=0;
	for(int i=0;i<len;i++)
	{
		index=s[i]-'a';
		while(p->next[index]==NULL&&p!=root)//若匹配失败,则找到失败指针指向的节点,直到成功匹配或到了root
			p=p->fail;
		if(p->next[index]!=NULL)
			p=p->next[index];

		//此处为找出当前状态下的所有模式串,记录并将找到的模式串标记,去重
		tmp=p;
		while(tmp!=root)
		{
			ans+=tmp->count;
			tmp->count=0;
			tmp=tmp->fail;
		}
	}
	return ans;
}

int main()
{
	int t,n;
	char word[55],text[maxn];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		cur=0;
		root=build_node();
		while(n--)//建字典树
		{
			scanf("%s",word);
			insert_tree(word);
		}
		BFS();//初始化失败指针
		scanf("%s",text);
		printf("%d\n",AC_AUTO(text));//匹配模式串
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值