P3796 【模板】AC自动机

传送门

AC自动机的模板

简单的理解就是字典树上的KMP

注意数组不要开太大

不然每次memset耗时太多

有一个小优化

每次走 fail 边找匹配时只有一些会更新答案

那么就可以把没用的fail边压缩掉

设 g[x] 表示从 x 点一直走 fail 边,走到的第一个有结束标记的点

那么找匹配时就只有要 g 边

然后就是模板了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=5e4+7;
int len,n;
char s[1000007];
inline void read_s()//文本串比较大,可以用快读加速
{
    char ch=getchar(); len=0;
    while(ch<'a'||ch>'z') ch=getchar();
    while(ch>='a'&&ch<='z')
    {
        s[++len]=ch;
        ch=getchar();
    }
}
int c[N][27],pd[N],fail[N],g[N],cnt;
char a[1670][670]; int l[570];
inline void clr()//清空AC自动机
{
    memset(c,0,sizeof(c));
    memset(pd,0,sizeof(pd));
    memset(fail,0,sizeof(fail));
    memset(g,0,sizeof(g));
    cnt=0;
}
inline void ins(int num)//插入单词到自动机(跟字典树没区别)
{
    int u=0;
    for(int i=0;i<l[num];i++)
    {
        int v=a[num][i]-'a'+1;
        if(!c[u][v]) c[u][v]=++cnt;
        u=c[u][v];
    }
    pd[u]=num;
}
queue <int> q;
inline void pre()//预处理fail和g
{
    for(int i=1;i<=26;i++)
        if(c[0][i]) q.push(c[0][i]);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=1;i<=26;i++)
        {
            int v=c[u][i];
            if(!v) c[u][i]=c[fail[u]][i];//失配了直接走失配边
            else
            {
                fail[v]=c[fail[u]][i];
                g[v]= pd[fail[v]] ? fail[v] : g[fail[v]];//处理g
                q.push(v);
            }
        }
    }
}
int mx,ans[167];
inline void query()//AC自动机匹配(好像跟字典树也没什么区别)
{
    memset(ans,0,sizeof(ans));
    int u=0;
    for(int i=1;i<=len;i++)
    {
        u=c[u][s[i]-'a'+1];
        if(pd[u]) ans[pd[u]]++;
        for(int j=g[u];j;j=g[j])
            ans[pd[j]]++;
        //计算匹配
    }
    mx=0;
    for(int i=1;i<=n;i++)
        mx=max(mx,ans[i]);
    cout<<mx<<endl;
    for(int i=1;i<=n;i++)
        if(ans[i]==mx)
            printf("%s\n",a[i]);
}
int main()
{
    while(1)
    {
        scanf("%d",&n); if(!n) break;
        clr();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",a[i]);
            l[i]=strlen(a[i]);
            ins(i);
        }
        pre();
        read_s();
        query();
    }
    return 0;
}

 

posted @ 2018-09-19 09:39  LLTYYC  阅读(218)  评论(0编辑  收藏  举报