题目传送门
题意
先给你n个模式串,然后再给你m个主串,问你每个主串中包括前面那几个模式串。
思路
AC自动机的简单变形(套模板稍加修改即可),注意有坑点,他说的是全部可见字符,共128个,而不是我们平常想的就默认那26个字母。(都快坑死了,一直wa… )
AC代码
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<stdlib.h>
using namespace std;
#define M 10010
struct trie{
int sign;//是否为该单词的最后一个结点
int fail;//失配指针
int next[130];//26个字母方向的子结点
}t[M<<4];
int q[M<<4],head,tail,L;
int an[M];
int shu;
char str[355],s[100000];
void Insert(char a[])//将单词插入字典树
{
int i=0,p=0,j,x;
while(a[i]){
x=t[p].next[a[i]];
if(x<0){//前面字符串未访问过此处,则申请新结点
t[p].next[a[i]]=x=++L;//数组模拟链表申请新结点(即++L操作)
for(j=0;j<128;j++)t[x].next[j]=-1;
t[x].fail=-1;t[x].sign=0;//初始化新结点信息
}
p=x;
i++;
}
t[p].sign=shu++;
//printf("%d**\n",shu);
}
void build_ACauto()//更新失配指针
{
int i,x,y,p;
t[0].fail=-1;
q[tail++]=0;//将根放入队列
while(head<tail){
x=q[head++];//取队首元素
for(i=0;i<128;i++){
y=t[x].next[i];
if(y>=0){
if(!x)t[y].fail=0;//如果x为根结点,那么他的子结点的失配指针为头结点
else{
p=t[x].fail;//取父结点的失配指针
while(p>=0){//如果失配指针不为空,继续找
if(t[p].next[i]>=0){//如果找到结点与相配
t[y].fail=t[p].next[i];//将失配指针指向它后退出循环
break;
}
p=t[p].fail;//否则继续往上找
}
if(p<0)t[y].fail=0;//如果最终还是没有找到,则失配指针指向根结点
}
q[tail++]=y;//将子结点存入队尾
}
else t[x].next[i]=t[t[x].fail].next[i];
}
}
}
int ACauto(char s1[] )//在字典树中查找s的子串在树中出现的次数
{
int i=0,j,p=0,x,num=0;
while(s1[i]){
j=s1[i];
while(t[p].next[j]<=0&&p)p=t[p].fail;//从字典树中找到相配结点或到达根时退出
p=t[p].next[j];//指向找到的结点所对应字母
if(p<=0)p=0;//如果没有找到,指针指向根结点
x=p;
while(x&&t[x].sign!=0){//如果不是根结点且未访问过,则继续查找
an[t[x].sign]=1;
num++;
//printf("%d**\n",t[x].sign);
//t[x].sign=-1;//把这句话去掉就可以把出现的全部次数都统计出来。
//例如 ch 在 chch里面 这道题是1个,有的可能要求两个,就要去掉
x=t[x].fail;//由失配指针向上查找
}
i++;
}
return num;
}
int main()
{
int T,n,i;
// scanf("%d",&T);
// while(T--){
shu=1;
int m;
head=tail=L=0;
t[0].fail=-1;//初始化头结点信息
t[0].sign=0;
for(i=0;i<128;i++)t[0].next[i]=-1;
scanf("%d",&n);
int nn=n;
while(n--){
scanf("%s",str);
Insert(str);//将读入的字符串插入字典树
}
build_ACauto();//更新字典树中的失配符
scanf("%d",&m);
int ans=0;
for(int i=1;i<=m;i++)
{
scanf("%s",s);
for(int j=1;j<=nn;j++)
{
an[j]=0;
}
int w=ACauto(s);
if(w)
{
printf("web %d:",i);
for(int j=1;j<=nn;j++)
{
if(an[j])
{
printf(" %d",j);
}
}
printf("\n");
ans++;
}
}
printf("total: %d\n",ans);//在字典树查找子串出现在字典树中的次数
// }
return 0;
}