hdu 2896 病毒侵袭 AC自动机入门题

点击打开链接

题意:给出若干个模式串n<=5e2长度<=2e2和主串个m<=1e3长度<=1e4 问每个主串中包含哪几个模式串,输出编号.

建立字典时 把单词结尾设为编号,设p[i]为模式串是在某个母串中出现,对每个母串跑AC自动机即可 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=201*500; 
int flag;
struct Aho{
	int chd[N][128],v[N],f[N],last[N],sz,ans;
	int p[N];//p[i] 模式串i是否出现 
	void init()
	{
		memset(p,0,sizeof(p));
		sz=1;ans=0;
		memset(v,0,sizeof(v));
		memset(f,0,sizeof(f));
		memset(chd[0],0,sizeof(chd[0]));
	}
	void insert(char *s,int x)
	{
		int u=0,i=0;
		while(s[i])
		{
			int id=s[i]-' ';
			if(!chd[u][id])
			{
				memset(chd[sz],0,sizeof(chd[sz]));
				chd[u][id]=sz++;
			}
			u=chd[u][id];
			i++; 
		}
		v[u]=x;//记录编号 
	}
	void getFail()
	{
		queue<int> q;
		f[0]=0;
		for(int c=0;c<128;c++)
		{
			int u=chd[0][c];
			if(u)
			{
				f[u]=0; q.push(u);last[u]=0;
			}
		}
		while(!q.empty())
		{
			int r=q.front();q.pop();
			for(int c=0;c<128;c++)
			{
				int u=chd[r][c];
				if(!u)
				{
					chd[r][c]=chd[f[r]][c];
					continue;
					//补边 
				}
				q.push(u);
				int x=f[r];
				//找到前缀i和后缀c相等 
				while(x&&!chd[x][c]) 
					x=f[x];
				f[u]=chd[x][c];
				//last作用:若到i匹配成功,将母串中以后缀i结尾的单词进行快速匹配
				//当母串中以后缀i的和某个模式串S匹配成功,则该模式串沿着失配边,找到前缀为模式串T,T和后缀s相同,同样也和母串后缀i相同 
				last[u]=v[f[u]]? f[u]:last[f[u]];
			}
		}
	}
	void solve(int j,int x)
	{
		if(!j) return;
		if(v[j])
		{
			flag++;
			p[v[j]]=1;//
		}
		solve(last[j],x);
	}
	void find(char *s,int x)
	{
		int n=strlen(s),j=0;
		for(int i=0;i<n;i++)
		{
			int id=s[i]-' ';
			j=chd[j][id];//trie
			if(v[j])
			solve(j,x);//匹配成功 看还有没有其他串和后缀i匹配
			else if(last[j])
			solve(last[j],x);// 
		}
	}	
}aho;
char s[10001];
int main()
{
	int n,m;
	while(cin>>n)
	{
		aho.init();
		char dic[600];
		int tot=0;	
		for(int i=1;i<=n;i++)
		{
			scanf("%s",dic);
			aho.insert(dic,i);
		}
		aho.getFail();
		cin>>m;
	
		for(int i=1;i<=m;i++)
		{	
			memset(aho.p,0,sizeof(aho.p));
			flag=0;
			int t=0;
			scanf("%s",s);
			aho.find(s,i);
			if(flag)
			{
				printf("web %d:",i);
				for(int k=1;k<=n;k++)
				{
					if(aho.p[k])
					{
						t++;
						printf(" %d",k);
						if(t>=flag)
						break;
					}
				}
				tot++;
				printf("\n");
			}	
		}
		printf("total: %d\n",tot);
	}
	return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值