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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值