计蒜客 蒜厂工作手册 (ac自动机)

 还是不很理解ac自动机的内涵,所以这道题卡住了,看了题解后恍然大悟。

因为是字典树匹配,接着用两个栈,一个存字符串,另外一个存节点。

这有什么用呢?原因是当找到一个单词的时候,就可以模拟删除这个单词,也就是从栈顶取出这个单词的所有字母跟节点序号。那么现在节点栈最顶端就是删除的这个单词的前一个字母的节点标号,再从这进行下一轮的匹配,当走完所有的字母就会发现字母栈中存储的正好就是删除剩下来的字母。

 

#include <iostream>
#include<cstring>

using namespace std;

const int MAX_N = 1e5+10;
const int MAX_C = 26;

int a;

struct AC_Automaton{
    int ch[MAX_N][MAX_C],fail[MAX_N],cnt[MAX_N];// ch 和 cnt 数组与 Trie 树中的一样;fail 保存的是失败指针。ch 和 fail 默认都为 -1
    int tot;  // Trie 树的总结点(不含根结点)个数
    int top;
    int stk[MAX_N];

    void init(){
        memset(ch,-1,sizeof(ch));
        memset(fail,0,sizeof(fail));
        tot=top=0;
        memset(cnt,0,sizeof(cnt));
    }

    void insert(char *str){
        int p=0;
        for(int i=0;str[i];i++){
            if(ch[p][str[i]-'a']==-1){
                ch[p][str[i]-'a']=++tot;
            }
            p=ch[p][str[i]-'a'];
        }
        cnt[p]=strlen(str);
    }

    void build(){
        int l=0,r=0,Q[MAX_N];
        for(int i=0;i<MAX_C;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<MAX_C;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];
                }
            }
        }
    }

    int count(char *str){
        int p=0;
        for(int i=0;str[i];i++){
            p=ch[p][str[i]-'a'];
            str[a++]=str[i];
            stk[top++]=p;
            if(cnt[p]){
                a-=cnt[p];
                top-=cnt[p];
                p=top?stk[top-1]:0;
            }
        }
    }

}ac;

char s[100006];
char ss[100006];

int main() {
    int n;
    ac.init();
    scanf("%s",ss);
    scanf("%d",&n);
    while(n--){
        scanf("%s",s);
        ac.insert(s);
    }
    ac.build();
    ac.count(ss);
    ss[a]='\0';
    printf("%s\n",ss);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值