【模板】AC自动机(简单版)

题目

有 NN 个由小写字母组成的模式串以及一个文本串 TT 。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 TT 中出现的次数最多。
https://www.luogu.org/problemnew/show/P3796

思路

trie树打错了,改了我几个小时。。。

其实,AC自动机只是在Trie树上做KMP罢了。

  1. 建树(不多说)
  2. 构建fail指针。Trie树的失配指针是指向:沿着其父节点 的 失配指针,一直向上,直到找到拥有当前这个字母的子节点 的节点 的那个子节点。复杂???对,我也是这么想的。看多几遍代码吧。
  3. 查询(不多说)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1e6+77;
struct AC
{
    int vis[27],fail,end;
}ac[maxn];
int n,cnt=0;
void build(char *c)
{
    int l=strlen(c),u=0;
    for(int i=0; i<l; i++)
    {
        char x=c[i];
        if(!ac[u].vis[x-'a']) ac[u].vis[x-'a']=++cnt;
        u=ac[u].vis[x-'a'];
    }
    ac[u].end+=1;
}
void getfail()
{
    queue<int> q;
    for(int i=0; i<26; i++)
    {
        if(ac[0].vis[i])
        {
            ac[ac[0].vis[i]].fail=0;
            q.push(ac[0].vis[i]);
        }
    }
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=0; i<26; i++)
        {
            if(ac[u].vis[i])
            {
                ac[ac[u].vis[i]].fail=ac[ac[u].fail].vis[i];
                                q.push(ac[u].vis[i]);
            }else
            ac[u].vis[i]=ac[ac[u].fail].vis[i];
        }
    }
}
int ACM(char *st)
{
    int l=strlen(st),u=0,ass=0;
    for(int i=0; i<l; i++)
    {
        u=ac[u].vis[st[i]-'a'];
        for(int t=u; t&&ac[t].end!=-1; t=ac[t].fail)
        {
            ass+=ac[t].end; ac[t].end=-1;
        }
    }
    return ass;
}
int main()
{
    char c[maxn];
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        scanf("%s",c);
        build(c);
    }
    ac[0].fail=0;
    getfail();
    scanf("%s",c);
    printf("%d",ACM(c));
}

猜你喜欢

转载自blog.csdn.net/Eric1561759334/article/details/80526380