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

一、题目:

洛谷原题

二、思路:

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

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

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

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

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

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

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

三、代码:

#include
#include
#include

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;iq;
    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;iq;
    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\n",vis[order[i]]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/little-aztl/p/11166428.html

你可能感兴趣的:(数据结构与算法)