暑假- ac自动机-(B - 病毒侵袭)

ac自动机入门题,但是还是写了很久。。。

小错误经常出现,比如数组开得太小,忘记初始化。。。

/*
题意:中文题,详见HDU 2896
思路:套AC自动机模板。
注意:建trie树时,字符串的信息(病毒编号)存放着最后一个字母的
value中,字符都是ASCII码可见字符,所以孩子节点有128个(0-127)
每匹配到一种病毒就用数组(web)存起来,最后一次性输出
*/
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int MAXM=100010;//树的最大节点(200*500)
const int MAXN=10010;//母串(匹配串)长度
const int ASCII=128;//ASCII的大小,即(0-127共128个)
char str[MAXN],	ch[205];//母串(网站串),病毒串
int tot=0,web[505];//包含病毒的网站个数,保存病毒编号
struct trie//字典树+AC自动机
{
	int root,trieN;//根节点,当前树的深度(节点个数)
	int child[MAXM][ASCII],value[MAXM],fail[MAXM];
	//分别为孩子节点,节点信息,失配指针
	int NewTrie()//构造新节点
	{
		value[trieN]=0;
		memset(child[trieN],-1,sizeof(child[trieN]));
		return trieN++;
	}
	void init()//初始化
	{
		trieN=0;
		root=NewTrie();
	}
	void Insert(char s[],int k)//建树,k为病毒编号
	{
		int x=root;
		for(int i=0;s[i];i++)
		{
			if(child[x][s[i]]==-1)//子树不存在时,构造新节点
			{
				child[x][s[i]]=NewTrie();
			}
			x=child[x][s[i]];//转移子树
		}
		value[x]=k;//在病毒串最后一个字母存放信息(编号)
	}
	void Build()//构建失配指针(fail)
	{
		int x;
		queue<int> q;
		fail[root]=root;
		for(int i=0;i<ASCII;i++)
		{
			if(child[root][i]==-1)
			{
				child[root][i]=root;
			}
			else
			{
				fail[child[root][i]]=root;
				q.push(child[root][i]);
			}
		}
		while(!q.empty())
		{
			x=q.front();q.pop();
			for(int i=0;i<ASCII;i++)
			{
				if(child[x][i]==-1)
				{
					child[x][i]=child[fail[x]][i];
				}
				else
				{
					fail[child[x][i]]=child[fail[x]][i];
					q.push(child[x][i]);
				}
			}
		}
	}	
	int query(char s[])//母串(网站串)匹配
	{
		int ans=0;
		int x=root,temp;
		memset(web,0,sizeof(web));
		for(int i=0;s[i];i++)
		{
			temp=x=child[x][s[i]];
			while(temp!=root)//利用fail指针匹配
			{
				if(value[temp]>0)//若有此种病毒(因为每个节点的value初始
				{                //值为0,编号至少为1)
					web[ans++]=value[temp];//把病毒信息(编号)
				}                          //存放着数组web中
				temp=fail[temp];
			}
		}
		return ans;//返回病毒个数
	}
}ac;
int main()
{
	int n,m;
	while(cin>>n)
	{
		getchar();
		ac.init();//初始化
		for(int i=1;i<=n;i++)
		{
			cin.getline(ch,205);//输入病毒(因为可能包含空格,所以用cin.getline)
			ac.Insert(ch,i);//建树
		}
		cin>>m;
		getchar();
		ac.Build();//构建fail
		for(int i=1;i<=m;i++)
		{
			cin.getline(str,MAXN);
			int ans=ac.query(str);//ans记录网站的病毒个数
			if(ans>0)//如果ans=0,即本网站无病毒,否则有病毒
			{
				cout<<"web "<<i<<":";//输出网站编号
				sort(web,web+ans);//按要求从小到大,排序
				for(int i=0;i<ans;i++)//输出病毒编号
				{
					cout<<' '<<web[i];
				}
				cout<<endl;
				tot++;//含病毒的网站+1
			}
		}
		cout<<"total: "<<tot<<endl;//输出一共包含病毒的网站个数
	}
	return 0;
}


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