• 洛谷 P5357 【模板】AC自动机(二次加强版)


    一、题目:

    洛谷原题

    二、思路:

    (我们将文本串叫做文章,模式串叫做单词。)

    那么这道题是个AC自动机的优化。我从题解上可以看出,这可能是个比较普通的优化。所以我决定再写一篇博客。

    那么我么考虑在匹配文章的过程中,我们是要不断跳到fail指针上去,而且一个点可能会被跳多次。那么这道题复杂度就爆炸了。

    考虑如何减少跳的次数。我们发现,所有的fail边同样构成了一棵树。那么如果我们记录下来每个节点在匹配文章的过程中被访问的次数num。那么我们发现在跳fail的过程中,i节点一共被访问的次数应该等于它的num加上所有指向它的fail指针的起点的num。语言表达可能有点不清楚,我们用公式表达。

    [i节点一共被访问的次数=num_i+sum_{fail[j]=i}num_j ]

    如果i节点是一个单词的末尾,那么i节点一共被访问的次数就是该单词在文章中出现的次数。

    所以这个问题就变得非常简单了。随便dfs一下,也可以直接从下往上更新一遍(拓扑排序)就可以了。

    三、代码:

    #include<iostream>
    #include<cstdio>
    #include<queue>
    
    using namespace std;
    
    const int maxn=2e5+5;
    
    int n;
    
    string T[maxn],S;
    
    int trie[maxn][30],sz,num[maxn*30],order[maxn],vis[maxn];
    int tag[maxn*30],fail[maxn*30];
    int in[maxn*30];
    
    inline void insert(string s,int id){
        int p=0;
        for(register int i=0;i<s.size();++i){
            if(!trie[p][s[i]-'a'+1])trie[p][s[i]-'a'+1]=++sz;
            p=trie[p][s[i]-'a'+1];
        }
        if(!tag[p])tag[p]=id;
        order[id]=tag[p];
    }
    
    inline void bfs(void){
        queue<int>q;
        for(register int i=1;i<=26;++i){
            if(trie[0][i])q.push(trie[0][i]),fail[trie[0][i]]=0;
        }
        while(q.size()){
            int x=q.front();q.pop();
            for(register int i=1;i<=26;++i){
                if(trie[x][i]){fail[trie[x][i]]=trie[fail[x]][i];in[fail[trie[x][i]]]++;q.push(trie[x][i]);}
                else trie[x][i]=trie[fail[x]][i];
            }
        }
    }
    
    inline void query(void){
        int p=0;
        for(register int i=0;i<S.size();++i){
            p=trie[p][S[i]-'a'+1];
            ++num[p];
        }
    }
    
    inline void topsort(void){
        queue<int>q;
        for(register int i=1;i<=sz;++i){
            if(!in[i])q.push(i);
        }
        while(q.size()){
            int x=q.front();q.pop();
            vis[tag[x]]=num[x];
            int y=fail[x];
            num[y]+=num[x];
            in[y]--;
            if(!in[y])q.push(y);
        }
    }
    
    int main(){
        cin>>n;
        for(register int i=1;i<=n;++i){
            cin>>T[i];
            insert(T[i],i);
        }
        cin>>S;
        bfs();
        query();
        topsort();
        for(register int i=1;i<=n;++i){
            printf("%d
    ",vis[order[i]]);
        }
        return 0;
    }
    
  • 相关阅读:
    python两个dict相加
    rpm命令
    python logging模块不支持多进程写到一个log文件
    技术论坛地址收集
    visudo命令编辑修改/etc/sudoers配置文件
    健康是什么
    .net文件类型种种
    禁止脚本的运行
    静态页面的值传递
    datagrid数据导出到excel文件给客户端下载的几种方法 (转)
  • 原文地址:https://www.cnblogs.com/little-aztl/p/11166428.html
Copyright © 2020-2023  润新知