HDU 2222 Keywords Search

ac自动机,大意是求一个长串中有含有给出的多少种串

照着标程打了一遍

思想和KMP差不多,关键是建立失配边,我们用BFS做这个

首先要建立trie树,第一次用指针

NULL是大写的,坑爹的我一开始全小写了

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct node
{
	node *fail;  //失配指针
	node *next[27];  
	int count;  //记录这个点是否有单词
	node()
	{
		fail=NULL;
		count=0;
		memset(next,NULL,sizeof(next));
	}
}*q[500001];
char s[1000010],ch;
int time,n,tot,i;
void insert(char *s,node *root,int l)  //trie树
{
	node *p=root;
	//int i=0;
	int posi;
	for(int i=1;i<=l;++i)
	{
		posi=s[i]-'a';
		if(p->next[posi]==NULL)p->next[posi]=new node();
		p=p->next[posi];
	}
	p->count++;
}
void ac(node *root)  //建立ac自动机
{
	int i;
	int wei=0,tou=0;
	root->fail=NULL;
	q[wei++]=root;
	while(tou<wei)//BFS
	{
		node *temp=q[tou++];
		node *p=NULL;
		for(i=0;i<26;++i)
		{
			if(temp->next[i]!=NULL)
			{
				if(temp==root) temp->next[i]->fail=root;
				else
				{
					p=temp->fail;
					while(p!=NULL)
					{
						if(p->next[i]!=NULL)  //如果p指针的下一个字母i有内容
						{
							temp->next[i]->fail=p->next[i];  //建立失配边
							break;
						}
						p=p->fail;
					}
					if (p==NULL) temp->next[i]->fail=root; //该节点未建立失配边则指向root
				}
				q[wei++]=temp->next[i];//下一层的加入队列
			}
		}
	}
}
int find(node *root,int l)  //匹配函数
{
	int ans=0,posi;
	node *p=root;
	for(int i=1;i<=l;++i)
	{
		posi=s[i]-'a';
		while(p->next[posi]==NULL&&p!=root)p=p->fail;
		p=p->next[posi];
		p=p==NULL?root:p;
		node *temp=p;
		while(temp!=root&&temp->count!=-1)//如果ac自动机里这个点不是根节点而且没到过
		{
			ans+=temp->count;
			temp->count=-1;  //避免重复计数
			temp=temp->fail;  //顺着失配边走
		}
	}
	return ans;//返回个数
}
int main()
{
	scanf("%d",&time);
	for(int t=1;t<=time;++t)
	{
		node *root=new node();
		scanf("%d",&n);
		ch=getchar();
		for(i=1;i<=n;++i)
		{
			tot=0;
			ch=getchar();
			while(ch!='\n')
			{
				s[++tot]=ch;
				ch=getchar();
			}
			insert(s,root,tot);
		}
		ac(root);
		tot=0;
		ch=getchar();
		while(ch!='\n')
		{
			s[++tot]=ch;
			ch=getchar();
		}
		printf("%d",find(root,tot));
	}
	return 0;
}
求大神指点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值