杭电2896题 AC自动机模板题



AC自动机的一个常见的例子就是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。


这题一定要注意建树时为128叉树:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
char number[1000005];
struct Trie
{
	int Next[100010][128],Fail[100010],End[100010];
	int root,L;
	int newnode()
	{
		for(int i=0;i<128;i++)
		{
			Next[L][i]=-1;
		}
			End[L++]=0;
			return L-1;
	}
	void init()
	{
		L=0;
		root=newnode();
	}
	void Insert(char s[],int n)         //插入单词
	{
		int len,i,now;
		len=strlen(s);
		now=root;
		for(i=0;i<len;i++)
		{
			if(Next[now][s[i]]==-1)
			{
				Next[now][s[i]]=newnode();
			}
			now=Next[now][s[i]];
		}
		End[now]=n;
	}
	void build()            //构造Fail指针
	{
		int i;
		queue<int>Q;
		Fail[root]=root;
		for(i=0;i<128;i++)
		{
			if(Next[root][i]==-1)
			Next[root][i]=root;
			else
			{
				Fail[Next[root][i]]=root;
				Q.push(Next[root][i]);
			}
		}
		while(!Q.empty())
		{
			int now=Q.front();
			Q.pop();
			for(int i=0;i<128;i++)
			{
				if(Next[now][i]==-1)
				Next[now][i]=Next[Fail[now]][i];
				else
				{
				Fail[Next[now][i]]=Next[Fail[now]][i];
				Q.push(Next[now][i]);
				}
			}
		}
	}
	bool Query(int T,int id)        //询问是否出现病毒,且将出现的病毒的序号输出
	{
		bool flag=false;
		bool used[600];
		memset(used,false,sizeof(used));
        int len=strlen(number);
		int now=root,i;
		int temp;
		for(i=0;i<len;i++)
		{
			now=Next[now][number[i]];
			temp=now;
			while(temp!=root)
			{
				if(End[temp]!=0)
                {
                    used[End[temp]]=true;
                    flag=true;
                }
				temp=Fail[temp];
			}
		}
		if(!flag)return false;
		printf("web %d: ",id);
		i=1;
		while(!used[i])i++;
		printf("%d",i);
		for(i=i+1;i<=T;i++)
        {
            if(used[i])printf(" %d",i);
        }
        printf("\n");
		return true;
	}
};
Trie ac;
int main()
{
	int T,i,n,sum,sum2;
	char buf[500010];
	while(~scanf("%d",&T))
    {
	sum2=0;
	ac.init();      //初始化
		for(i=1;i<=T;i++)
		{
			scanf("%s",buf);    //读取单词
			ac.Insert(buf,i);   //将单词存入树中
		}
		ac.build();             //建立Fail指针
		scanf("%d",&n);
		for(i=1;i<=n;i++)
        {
		scanf("%s",number);         //n个网站,每个都要进行查询
		if(ac.Query(T,i))
        {
            sum2++;     //统计带病毒的网站数
        }
        }
        printf("total: %d\n",sum2);
    }
return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值