P3796 【模板】AC自动机(加强版)

题目描述

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

Solution:

  额,还是比较板子的AC自动机。

  略过AC自动机的构建(本题最好搞个后缀链接的优化),我们只需要在匹配的时候,记录一下各字符串出现的次数,最后比较一下就好了,坑点是除了字符串其它所有数组都要清0(trie,end,fail,last,cnt,num)。

  其实本题原题蓝书上有,那里会有重复的模式串,而本题没有,重复的模式串会在trie树中覆盖掉前面出现的字符串的end,可以思考一下加加强版,实现方法很多。

代码:

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=10305,M=1000005;
int trie[N][26],cnt,end[N],n,num[155],last[N],fail[N];
char s[155][72],t[M];

il void cler(){
    memset(trie,0,sizeof(trie));
    memset(num,0,sizeof(num));
    memset(end,0,sizeof(end));
    memset(fail,0,sizeof(fail));
    memset(last,0,sizeof(last));
    cnt=0;
}

il void insert(char *s,int id){
    int len=strlen(s)-1,p=0,x;
    For(i,0,len){
        x=s[i]-'a';
        if(!trie[p][x])trie[p][x]=++cnt;
        p=trie[p][x];
    }
    end[p]=id;
}

il void bfs(){
    queue<int>q;
    For(i,0,25) if(trie[0][i]) fail[trie[0][i]]=0,q.push(trie[0][i]);
    while(!q.empty()){
        int u=q.front();q.pop();
        For(i,0,25){
            int v=trie[u][i];
            if(v) fail[v]=trie[fail[u]][i],last[v]=end[fail[v]]?fail[v]:last[fail[v]],q.push(v);
            else trie[u][i]=trie[fail[u]][i];
        }
    }
}

il void query(char *t){
    int len=strlen(t)-1,p=0,ans=0;
    For(i,0,len){
        p=trie[p][t[i]-'a'];
        for(int j=p;j;j=last[j]) if(end[j]) num[end[j]]++;
    }
    For(i,1,n) ans=max(num[i],ans);
    printf("%d
",ans);
    For(i,1,n) if(num[i]==ans) puts(s[i]);
}

int main(){
    while(scanf("%d",&n)==1){
        if(!n) exit(0);
        cler();
        For(i,1,n) scanf("%s",s[i]),insert(s[i],i);
        bfs();
        scanf("%s",t);
        query(t);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/five20/p/9443815.html