zoukankan      html  css  js  c++  java
  • 字符串模板--AC自动机

    前言:学习AC自动机


    1.AC自动机?

    AC自动机简称为树上KMP,这里的树是指trie树,KMP是字符串单一匹配的一种方法.学习AC自动机的前提是这两个算法都要掌握.AC自动机作为两者有机的结合,一定程度上弥补了KMP算法对单一匹配的不足(nxt数组只能对应一个匹配串),在trie树上能实现多对一的匹配.想想利用KMP算法我们要对每一个匹配串建立nxt数组,在trie树上利用fail指针即可.算法效率可提高.

    2.AC自动机的构建.

    I.构建trie树.
    没错先构建trie树,但注意是对匹配串构建(肯定啊,主串只有一条)
    II.构建fail指针.
    跟KMP中nxt数组类似,fail数组也指字符最长前缀等于后缀的长度.那么在trie树上的''nxt''数组怎么求解呢?这里我们用BFS实现.首先将root定义为0,我们将与root直接相连的child的fail指向root,并加入队列,由第一层的child就此扩展.以后每扩展节点,它拥有26child的实点或者虚点.对于实点求fail,我们看它的father指向的fail节点的直接child看是否相同,有就直接指向它,没有则跳向fail的fail,看它的直接child找不找的相同的...在这个过程中最坏情况最终指向根结点.对于虚点的话,我们直接将它指向father的fail对应child.为什么这做在匹配时解释.code:
    void getfail()
    {
        queue<int> q;
        for(R int i=0;i<26;++i){
            if(tire[0][i]){
                fail[tire[0][i]]=0;
                q.push(tire[0][i]);
            }
        }
        while(q.size()){
            int now=q.front();
            q.pop();
            for(R int i=0;i<26;++i){
                if(tire[now][i]){
                    fail[tire[now][i]]=tire[fail[now]][i];
                    q.push(tire[now][i]);    
                }
                else tire[now][i]=tire[fail[now]][i];
            }
        }
    }

    III.字符串匹配.

    这次我们是将主串的每一字符放在trie树上跑,每次对现在的id跳fail,这样我们跑着在匹配串中的相同字符串都可跑到,在这里我们对虚点就可直接跳fail(当主串失配时),这样我们就不用对失配的地方从头开始匹配.此时其实不是trie树是trie图了.以LG板子为例,code:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    using namespace std;
    #define e exit(0)
    #define R register
    const int maxn=2*1e6+10;
    char t[maxn],s[maxn];
    int n,lent,lens,cnt,tire[10000007][27],fail[maxn],cntword[maxn<<1],ans;
    void insert(string t)
    {
        int root=0;
        for(R int i=0;i<lent;++i){
            int word=t[i]-'a';
            if(!tire[root][word])
                tire[root][word]=++cnt;
            root=tire[root][word];
        }
        ++cntword[root];
    }
    void getfail()
    {
        queue<int> q;
        for(R int i=0;i<26;++i){
            if(tire[0][i]){
                fail[tire[0][i]]=0;
                q.push(tire[0][i]);
            }
        }
        while(q.size()){
            int now=q.front();
            q.pop();
            for(R int i=0;i<26;++i){
                if(tire[now][i]){
                    fail[tire[now][i]]=tire[fail[now]][i];
                    q.push(tire[now][i]);    
                }
                else tire[now][i]=tire[fail[now]][i];
            }
        }
    }
    int ask(string s)
    {
        ans=0;
        int now=0;
        for(R int i=0;i<lens;++i){
            int word=s[i]-'a';
            now=tire[now][word];
            for(R int j=now;j&&cntword[j]!=-1;j=fail[j]){
                ans+=cntword[j];
                cntword[j]=-1;
            }
        }
        return ans;
    }
    int main()
    {
    //    freopen("s.in","r",stdin);
    //    freopen("s.out","w",stdout);
        scanf("%d",&n);
        while(n--){
            scanf("%s",t);
            lent=strlen(t);
            insert(t);
        }
        fail[0]=0;
        getfail();
        scanf("%s",s);
        lens=strlen(s);
        printf("%d",ask(s));
        return 0;
    }
  • 相关阅读:
    laravel队列
    php程序内存优化之数组操作优化
    swoole4创建Mysql连接池
    MySQL创建索引
    mysql索引命中规则
    Redis数据类型及使用场景
    Redis高可用集群-哨兵模式(Redis-Sentinel)
    网站架构优化性能
    PHP实现Redis分布式锁
    微软公司面试题
  • 原文地址:https://www.cnblogs.com/xqysckt/p/11244880.html
Copyright © 2011-2022 走看看