题目
思路来源
https://www.cnblogs.com/hefenghhhh/p/5051334.html
注意事项:
①MLE 32768KB卡限,这里用指针的AC自动机卡了32628KB过了,考虑用数组的AC自动机
②病毒小于等于3个也得排序啊
③在线输出和离线输出都可以
AC代码1:
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <bitset>
const int INF=0x3f3f3f3f;
const int mod=10007;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pii pair<int,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
//ascii码可见 开128数组比较靠谱 扩容 注意26只适用于小写字母的情况
//这题有毒 多组输入没说 稍微开大一点空间就MLE 32768kb异常的坑
struct acnode{
int sum;
int v;//代表是几号串的标号
acnode* next[128];
acnode* fail;
acnode(){
v=0;
for(int i =0;i<128;i++)
next[i]=NULL;
fail= NULL;
sum=0;
}
};
acnode *root;
char s[10005],tmp[205];//搞离线
int ans[15],n,m,final,mark[505];
acnode* newnode()
{
acnode *p = new acnode();
return p;
}
//插入函数
void Insert(char *s,int num)
{
acnode *p = root;
for(int i = 0; s[i]; i++)
{
int x = s[i];
if(p->next[x]==NULL)
{
acnode *nn=newnode();
p->next[x]=nn;
}
p = p->next[x];
}
p->sum++;
p->v=num;
}
//获取fail指针,在插入结束之后使用
void getfail(){
queue<acnode*> q;
for(int i = 0 ; i < 128 ; i ++ )
{
if(root->next[i]!=NULL){
root->next[i]->fail = root;
q.push(root->next[i]);
}
}
while(!q.empty())
{
acnode* tem = q.front();
q.pop();
for(int i = 0;i<128;i++)
{
if(tem->next[i]!=NULL)
{
acnode *p;
if(tem == root)
{
tem->next[i]->fail = root;
}
else
{
p = tem->fail;
while(p!=NULL){
if(p->next[i]!=NULL){
tem->next[i]->fail = p->next[i];
break;
}
p=p->fail;
}
if(p==NULL)
tem->next[i]->fail = root;
}
q.push(tem->next[i]);
}
}
}
}
//匹配函数
int ac_automation(char *ch)
{
acnode *p = root;
int len = strlen(ch),tot=0;
for(int i = 0; i < len; i++)
{
int x = ch[i];
while(p->next[x]==NULL && p != root)//没匹配到,那么就找fail指针。
p = p->fail;
p = p->next[x];
if(!p)
p = root;
acnode *temp = p;
while(temp != root)
{
if(temp->sum >= 0)
{
if(temp->v&&!mark[temp->v])//标记一下不然可能会影响结果 别的失配的时候正好路过该单词尾
{
ans[tot++]=temp->v;
mark[temp->v]=1; //匹配过,不该改为0,不然下次就没法匹配了
}
}
else break;
temp = temp->fail;
}
}
return tot;
}
void Delete(acnode* root)
{
for(int i=0;i<128;++i)if(root->next[i])Delete(root->next[i]);
delete root;
}
void init()
{
mem(ans,0);
final=0;
}
int main()
{
while(~scanf("%d",&n))
{
init();
root=newnode();
sci(n);
rep(i,1,n)
{
scanf("%s",tmp);
Insert(tmp,i);
}
getfail();
sci(m);
rep(i,1,m)
{
scanf("%s",s);
int num=ac_automation(s);
if(num)sort(ans,ans+num);//坑比的排序,忽视了一直wa
rep(j,0,num-1)
{
if(!j)final++,printf("web %d:",i);
printf(" %d",ans[j]);
if(j==num-1)puts("");
}
mem(mark,0);
}
printf("total: %d\n",final);
Delete(root);
}
return 0;
}
代码2(数组版本 当模板用)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
int id,fail,Next[130];
}trie[100010];
char word[202],web[10005];
int que[100010];//建ac自动机的队列
int root,tot;//根的编号 节点编号
bool ok[502];//i病毒是否出现
int Virus,Webs;
int n,m,res[10];
void insert(int r,char *s,int id)//(root,串,串号),trie树插入
{
int len=strlen(s);
for(int i=0;i<len;i++)
{
if(trie[r].Next[s[i]-32]==0)
trie[r].Next[s[i]-32]=++tot;
r=trie[r].Next[s[i]-32];
}
trie[r].id=id;
}
void build(int r)//建ac自动机
{
int head=0,tail=0;
trie[r].fail=r;//root自己连自己
que[tail++]=r;
while(head<tail)
{
r=que[head++];
for(int i=0;i<130;i++)
{
int ch=trie[r].Next[i],failp;
if(ch)
{
que[tail++]=ch;
for(failp=trie[r].fail;failp!=root&&trie[failp].Next[i]==0;failp=trie[failp].fail);//向前回跳
if(trie[failp].Next[i]==0||trie[failp].Next[i]==ch)trie[ch].fail=failp;//接到根或者自己
else trie[ch].fail=trie[failp].Next[i];//可以后接
}
}
}
}
void query(int r,char *s)
{
int len=strlen(s);
for(int i=0;i<len;i++)
{
while(trie[r].Next[s[i]-32]==0&&r!=root)r=trie[r].fail;
r=trie[r].Next[s[i]-32]?trie[r].Next[s[i]-32]:root;//可以向后走就向后走,从root向后走也行,否则就是root;
for(int tempfail=r;tempfail!=root;tempfail=trie[tempfail].fail)//对自动机上的一段类似KMP
{
if(ok[trie[tempfail].id])break;//这说明子段之前都被统计了
if(trie[tempfail].id>0)ok[trie[tempfail].id]=1;
}
}
}
int main()
{
root=++tot;//从1起,与0区分
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s",word);
insert(root,word,i+1);
}
build(root);
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%s",web);
memset(ok,0,sizeof ok);
query(root,web);
Virus=0;
for(int k=0;k<502;k++)
if(ok[k])res[Virus++]=k;
if(Virus)
{
Webs++;
printf("web %d:",i+1);
for(int k=0;k<Virus;k++)
printf(" %d",res[k]);
puts("");
}
}
printf("total: %d\n",Webs);
return 0;
}