LA4670 AC自动机模版题

这里有个大神的博客:http://www.cnblogs.com/yefeng1627/archive/2013/05/23/3094566.html

题意:给你一些模式串和一个文本串,求在文本串中出现次数最多的模式串,输出次数和这些模式串。

文本串很长,模式串数量多但长度短,适用于AC自动机。

第一写AC自动机,基本照着敲的,不过稍微懂了些,希望早点掌握啦。

这题直接先建一个AC自动机,把串都编号,用map来防重复,在AC自动机里用一个cnt数组记录所有串的编号的出现次数,最后遍历所有串的边号,找出最多的次数,然后在遍历所有的字符串,找出串编号出现次数等于最大值的串。

代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<string>
#include<map>
#include<queue>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 10005
#define INF 0xfffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(i,s,t) for(int i=s;i<=t;i++)
#define ull unsigned long long
#define ll long long
#define N 20000
#define M 1000006
using namespace std;
struct AC
{
    int ch[N][26],f[N],last[N];
    int val[N],cnt[N];
    int top;
    void init()//基本都得置0
    {
        top=0;
        memset(ch,0,sizeof(ch));
        memset(f,0,sizeof(f));
        memset(last,0,sizeof(last));
        memset(val,0,sizeof(val));
        memset(cnt,0,sizeof(cnt));
    }
    int NewNode()
    {
        int x=++top;
        return x;
    }
    void insert(char *s,int num)//传参串的编号,类似trie的构建
    {
        int l=strlen(s);
        int p=0;
        for(int i=0; i<l; i++)
        {
            int c=s[i]-'a';
            if(ch[p][c]==0)
            {
                ch[p][c]=NewNode();
            }
            p=ch[p][c];
        }
        val[p]=num;
    }
    void getfail()//求每个结点的f数组和last数组,分别表示失配指针和失配的模式串指针
    {
        queue<int> q;
        for(int c=0; c<26; c++)//初始化所有只有一个字符的节点的失配指针都指向树根
        {
            int u=ch[0][c];
            if(u)
            {
                f[u]=0;
                last[u]=0;
                q.push(u);
            }
        }
        while(!q.empty())
        {
            int r=q.front();
            q.pop();
            for(int c=0; c<26; c++)
            {
                int u=ch[r][c];
                if(!u)//如果r没有c这个子孙,那么就连到与r的失配指针指向的节点的c子孙
                {
                    ch[r][c]=ch[f[r]][c];
                    continue;
                }
                q.push(u);//如果该节点存在,更新f和last数组
                int v=f[r];
                while(v&&!ch[v][c]) v=f[v];//更新f
                f[u]=ch[v][c];
                last[u]=val[f[u]]?f[u]:last[f[u]];//更新last
            }
        }
    }
    void find(char *s)
    {
        int n=strlen(s);
        int p=0;
        for(int i=0; i<n; i++)
        {
            int c=s[i]-'a';
            p=ch[p][c];//这里直接匹配,可能匹配成功,也可能失败
            if(val[p]) count(p);//如果匹配成功,并且有这个串,就计数
            else if(last[p])//如果改点不是模式串,那就看他的失配指针
            {
                count(last[p]);
            }
        }
    }
    void count(int x)
    {
        if(x)//如果改点是模式串就计数,并且把有相同后记的所有节点递归计数
        {
            cnt[val[x]]++;
            if(last[x])
            {
                count(last[x]);
            }
        }
    }
} ac;
map<char*,int> mp;
char text[M];
char str[155][77];
int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        if(!n) break;
        mp.clear();
        ac.init();
        int tot=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%s",str[i]);
            if(mp.count(str[i])==0) mp[str[i]]=++tot;
            ac.insert(str[i],mp[str[i]]);
        }
        ac.getfail();
        scanf("%s",text);
        ac.find(text);
        int k=0;
        for(int i=1; i<=tot; i++)
        {
            k=max(k,ac.cnt[i]);
        }
        printf("%d\n",k);
        for(int i=1; i<=n; i++)
        {
            if(ac.cnt[mp[str[i]]]==k)
            {
                printf("%s\n",str[i]);
            }
        }
    }
    return 0;
}



你可能感兴趣的:(LA4670 AC自动机模版题)