题意:有N个病毒特征码(编号从1——N),M个网站源码(编号从1——M)。问每一个网站源码中含有哪些病毒特征码,输出特征码编号(没有就不输出)。最后输出总共有多少个网站有病毒。
思路:AC自动机裸题,主要是要理解AC自动机的思想,他是一种解决多模式匹配的算法,fail指针的作用类似于KMP中的nextval数组的作用,是当前位置失配后,下一个字符比较的位置。 大佬讲解(结合图片中的例子看)
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
bool vis[501];
struct node //字典树结点的结构体
{
int id;
node *next[95];
node *fail; //失配指针
node()
{
id=-1; //在字符串结束的结点处记录该字符串id
fail=NULL;
memset(next,0,sizeof(next));
}
};
void Insert(char *s,node *root,int id) //构建字典树
{
node *p=root;
for(int i=0;s[i];i++)
{
int x=s[i]-' ';
if(p->next[x]==NULL)
p->next[x]=new node();
p=p->next[x];
}
p->id=id;
}
void ac_automation(node *root) //添加fail指针
{
queue<node*>que;
que.push(root);
while(!que.empty())
{
node *p=que.front();
que.pop();
node *tmp=NULL;
for(int i=0;i<95;i++)
{
if(p->next[i]!=NULL)
{
if(p==root)
p->next[i]->fail=root;
else
{
tmp=p->fail;
while(tmp!=NULL)
{
if(tmp->next[i]!=NULL)
{
p->next[i]->fail=tmp->next[i];
break;
}
tmp=tmp->fail;
}
if(tmp==NULL)
p->next[i]->fail=root;
}
que.push(p->next[i]);
}
}
}
}
bool query(char *s,node *root)
{
bool flag=false;
node *p=root;
for(int i=0;s[i];i++)
{
int j=s[i]-' ';
while(p->next[j]==NULL&&p!=root) //没匹配上
p=p->fail;
p=p->next[j];
if(p==NULL)
p=root;
node *tmp=p;
while(tmp!=root)
{
if(tmp->id!=-1)
{
vis[tmp->id]=true;
flag=true;
}
tmp=tmp->fail;
}
}
return flag;
}
int main()
{
int n,m;
scanf("%d",&n);
node *root=new node();
char s[201];
for(int i=0;i<n;i++)
{
scanf("%s",s);
Insert(s,root,i);
}
ac_automation(root);
scanf("%d",&m);
int num=0;
char s1[10001];
vector<int>v[1001];
for(int i=0;i<m;i++)
{
scanf("%s",s1);
memset(vis,false,sizeof(vis));
bool flag=query(s1,root);
if(flag)
{
num++;
for(int j=0;j<n;j++)
if(vis[j])
v[i].push_back(j+1);
}
}
for(int i=0;i<m;i++)
{
if(!v[i].empty())
{
printf("web %d:",i+1);
for(int j=0;j<v[i].size();j++)
printf(" %d",v[i][j]);
printf("\n");
}
}
printf("total: %d\n",num);
return 0;
}