hdu5880-ac自动机,超详解析

题意是:给你一些模式串,再给一个文本串,如果在文本串中能够查询到该模式串,就将文本串中的模式串改为 '*'

注意可能会爆内存和字符串的输入。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1000005;
struct AC{
    int ch[maxn][26];
    int cnt[maxn];                   //记录模式串的长度
    int fail[maxn];                  //失败指针
    int tot;
	int q[maxn];                    //q[maxn]在build方法中使用,建立失败指针时会用到,功能和队列差不多,
    void init(){                    //放在build()方法里面,开的数组太大,导致数组溢出,就放结构体了
        memset(ch[0],-1,sizeof(ch[0]));
        memset(cnt,0,sizeof(cnt));
        memset(fail,0,sizeof(fail));
        tot=0;
    }
    int newnode()
    {
        tot++;
        memset(ch[tot],-1,sizeof(ch[tot]));
        fail[tot]=0;
        cnt[tot]=0;
        return tot;
    }
    void insert(char *s){
        int p=0;
        for(int i=0;s[i];i++){
            if(ch[p][s[i]-'a']==-1)
                ch[p][s[i]-'a']=newnode();
            p=ch[p][s[i]-'a'];
        }
        cnt[p]=strlen(s);            //记录下该模式串的长度,在接下来的文本串查找中,将会使用。
    }
    /*void insert(char *s){          //一开始选择使用这种插入方法,导致内存直接溢出,大概84000k
        int p=0;                     //,换了插入之后48000k如果有更好的插入方法,欢迎指出
        for(int i=0;s[i];i++){
            if(ch[p][s[i]-'a']==-1)
                ch[p][s[i]-'a']=++tot;
            p=ch[p][s[i]-'a'];
        }
        cnt[p]=strlen(s);
    }*/
    void build(){                  //建立失败指针,一般使用队列,怎么说,用l和r这种方法和队列各有好坏吧!
        int l=0,r=0;
        for(int i=0;i<26;i++){
            if(ch[0][i]==-1)
                ch[0][i]=0;
            else{
                q[r++]=ch[0][i];
            }
        }
        while(l<r){
            int p=q[l++];
            for(int i=0;i<26;i++){
                if(ch[p][i]==-1){
                    ch[p][i]=ch[fail[p]][i];
                }else{
                    fail[ch[p][i]]=ch[fail[p]][i];
                    q[r++]=ch[p][i];
                }
            }
        }
    }
    void count(char *s){
        int p=0;
        for(int i=0;s[i];i++){
            if(s[i]>='A'&&s[i]<='Z'||s[i]>='a'&&s[i]<='z')
            {
                if(s[i]>='A'&&s[i]<='Z')
                    p=ch[p][s[i]-'A'];
                else
                    p=ch[p][s[i]-'a'];
                int tmp=p;
                while(tmp!=0){                  //如果存在该模式串
                    for(int j=0;j<cnt[tmp];j++)//从模式串的末尾,依次改到模式串的首位
                        s[i-j]='*';
                    tmp=fail[tmp];
                }
            }
        }
    }
}ac;
char str[1000005],s[1000005];
int main()
{
    freopen("1.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--){
        ac.init();
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",s);
            ac.insert(s);
        }
        ac.build();
        getchar();                  //getchar的意义在于隔绝前面的输入,如果没有的话,gets不能输入的
        gets(str);
        ac.count(str);
        printf("%s",str);
        printf("\n");
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值