【AC自动机】Lougu P3796

题目描述

NNN个由小写字母组成的模式串以及一个文本串TTT。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串TTT中出现的次数最多。

输入输出格式

输入格式:

输入含多组数据。

每组数据的第一行为一个正整数NNN,表示共有NNN个模式串,1≤N≤1501 \leq N \leq 1501N150。

接下去NNN行,每行一个长度小于等于707070的模式串。下一行是一个长度小于等于10610^6106的文本串TTT。

输入结束标志为N=0N=0N=0。

输出格式:

对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。

输入输出样例

输入样例#1:
2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0
输出样例#1:
4
aba
2
alpha
haha

题解

写个AC自动机的板子上来。。。

反正就是先建一棵trie树,然后bfs找失配指针(类似KMP)。。。

然后再在上面搞些奇奇怪怪的东西。。。

代码

//by 减维
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<map>
#include<bitset>
#include<algorithm>
#define ll long long
#define maxn 1000005
using namespace std;

struct trie{
    int end,fail,to[26];
    void cle(){
        memset(to,0,sizeof(to));
        end=fail=0;
    }
}ac[maxn];

struct anss{
    int pos,num;
}ans[maxn];

int n,tot;
string s[155];

bool cmp(const anss&x,const anss&y)
{
    if(x.num==y.num)return x.pos<y.pos;
    return x.num>y.num;
}

void build(int x)
{
    int now=0,ch;
    int len=s[x].length();
    for(int i=0;i<len;++i)
    {
        ch=s[x][i]-'a';
        if(!ac[now].to[ch])ac[now].to[ch]=++tot,ac[tot].cle();
        now=ac[now].to[ch];
    }
    ac[now].end=x;
}

void getf()
{
    queue<int>q;
    for(int i=0;i<26;++i)
        if(ac[0].to[i])q.push(ac[0].to[i]),ac[ac[0].to[i]].fail=0;
    while(!q.empty())
    {
        int d=q.front();
        q.pop();
        for(int i=0;i<26;++i)
        {
            int dd=ac[d].to[i];
            if(dd)ac[dd].fail=ac[ac[d].fail].to[i],q.push(dd);
            else ac[d].to[i]=ac[ac[d].fail].to[i];
        }
    }
}

void ask()
{
    int now=0,ch;
    int len=s[0].length();
    for(int i=0;i<len;++i)
    {
        ch=s[0][i]-'a';
        now=ac[now].to[ch];
        for(int j=now;j;j=ac[j].fail)
            if(ac[j].end)ans[ac[j].end].num++;
    }
    sort(ans+1,ans+1+n,cmp);
    printf("%d\n",ans[1].num);
    for(int i=1;i<=n;++i)
        if(ans[i].num==ans[1].num)cout<<s[ans[i].pos]<<endl;
        else break;
}

int main()
{
    while(1)
    {
        scanf("%d",&n);
        if(n==0)return 0;
        tot=0;ac[0].cle();
        for(int i=1;i<=n;++i)
        {
            cin>>s[i];
            build(i);
            ans[i].pos=i;
            ans[i].num=0;
        }
        getf();
        cin>>s[0];
        ask();
    }
}

 

posted @ 2017-12-08 18:04  减维  阅读(228)  评论(0编辑  收藏  举报