• AC自动机


    //AC自动机 
    //相当于在Trie树上求KMP 
    //可以求一个字符串的多个匹配子串 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    struct uio{
        int son[27],end,fail;//fail指针相当于KMP中的next[]数组 
    }trie[1000001];
    int n,cnt;
    char c[1000001];
    void insert(char* a)
    {
        int now=0,len=strlen(a);
        for(int i=0;i<len;i++)
        {
            int c=int(a[i]-'a');
            if(!trie[now].son[c])
                trie[now].son[c]=++cnt;
            now=trie[now].son[c];
        }
        trie[now].end+=1;
    }
    void get_fail()//求fail指针 
    {
        queue<int> que;
        while(!que.empty()) que.pop();
        //将根节点的fail指针预处理出来
        //原因是根节点的fail指针指向了根节点自身
        //若在BFS中处理会导致其儿子也将fail指针指向儿子自身 
        for(int i=0;i<26;i++)
            if(trie[0].son[i])
            {
                trie[trie[0].son[i]].fail=0;
                que.push(trie[0].son[i]);
            }
        while(!que.empty())//BFS 
        {
            int now=que.front();
            que.pop();
            //在取出节点后,正确的操作应为: 
            //循环26个字母,若不存在儿子则continue 
            //否则查询:表示字母sth的儿子(下文称sth)的父亲(也就是刚从队列中取出的节点,下文称sth_fa), 
            //其fail指针指向的节点(下文称sth2_fa)是否也有一个表示字母sth的儿子(下文称sth2) 
            //若有则直接将sth的fail指针指向sth2 
            //否则查询sth2_fa的fail指针指向的节点是否也有一个表示字母sth的儿子 
            //重复以上过程直至找到一个符合条件的儿子或是回溯到root节点 
            //对于后者直接将sth的fail指针指向root即可 
            for(int i=0;i<26;i++)
            {
                if(trie[now].son[i])
                {
                    trie[trie[now].son[i]].fail=trie[trie[now].fail].son[i];
                    que.push(trie[now].son[i]);
                }
                else trie[now].son[i]=trie[trie[now].fail].son[i];
            }
            //那么以上代码为什么正确呢 
            //考虑分3种情况 
            //1.有此儿子且第一次查询就找到应指向的节点 则直接指向即可 
            //2.有此儿子但第一次查询找到了一个空节点 则直接指向即可 
            //原因是在遇到空节点时会将它的fail指针指向
            //其父亲的fail指针指向的节点的表示相同字母的儿子 
            //这就相当于做了本应在递归过程中进行的操作 
            //3.无此儿子 这种情况的操作不再赘述 
        }
    }
    void query(char* a)
    {
        int now=0,len=strlen(a);
        int ans=0;
        for(int i=0;i<len;i++)
        {
            now=trie[now].son[a[i]-'a'];
            for(int j=now;j&&trie[j].end!=-1;j=trie[j].fail)
            //未回溯至根节点且未查询过 
                ans+=trie[j].end,trie[j].end=-1;
        }
        printf("%d
    ",ans);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%s",c),insert(c);
        trie[0].fail=0;//标志结束 
        get_fail();
        scanf("%s",c);
        query(c);
        return 0;
    }
  • 相关阅读:
    linux系统中完整删除逻辑卷、卷组、物理劵
    linux系统中逻辑卷缩容步骤
    问题
    新的一天开始了
    Solaris的版本命名
    Sun公司的操作系统:Solaris简介
    培训经历:Awakening the Dragon Within
    大本计算机专业该怎么学习?
    windbg 使用
    ubuntu 常用技巧
  • 原文地址:https://www.cnblogs.com/water-radish/p/9325459.html
Copyright © 2020-2023  润新知