HDU-2896病毒侵袭(AC自动机)

题目传送门
在这里插入图片描述
题意
先给你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;
}

发布了42 篇原创文章 · 获赞 5 · 访问量 927

猜你喜欢

转载自blog.csdn.net/qq_43402296/article/details/103925076
今日推荐