LA4670 AC自动机

来源:互联网 发布:log4j sql日志级别 编辑:程序博客网 时间:2024/06/15 20:51


分析:本题模板串多且长度短,文本串却很长,适合使用AC自动机。

一个容易忽略的地方是重复出现的模板,如果有模板重复,后一个子串会覆盖前一个子串。因此要建立一个字符串到编号的索引map<string,int>mp,每次初始化时候清空。


代码如下:

#include <cstring>#include <cstdio>#include <queue>#include <map>#include <string>using namespace std;const int sigma_size = 26;const int maxs = 150+10;const int maxn = 11000;map<string,int>mp;queue<int>que;int ch[maxn][sigma_size];int fail[maxn]; //fail函数int val[maxn]; //每个字符串的结尾结点都有一个非0的valint last[maxn]; //输出链表的下一个结点int cnt[maxs];char p[maxs][80];char text[1000000+10];int sz;int N;int idx(char c) { return c-'a';}//插入字符串,v必须非零void Insert(char *s, int v){   int u = 0, len = strlen(s);   int c;   for (int i=0; i<len; i++){       c = idx(s[i]);       if (!ch[u][c]) {          memset(ch[sz],0,sizeof(ch[sz]));          val[sz] = 0;          ch[u][c] = sz++;       }       u = ch[u][c];   }   val[u] = v;   mp[string(s)] = v;}void init(){    sz = 1;    memset(ch[0],0,sizeof(ch[0]));    memset(cnt,0,sizeof(cnt));    mp.clear();    for (int i=1; i<=N; i++) {        scanf("%s",p[i]);        Insert(p[i],i);    }}void getfail(){    while(!que.empty()) que.pop();    fail[0] = 0;    int u,r,v;    for (int i=0; i<sigma_size; i++) {         u = ch[0][i];         if (u) {fail[u] = 0; que.push(u); last[u] = 0;}    }    while (!que.empty()){        r = que.front(); que.pop();        for (int c=0; c<sigma_size; c++){            u = ch[r][c];            if (!u) continue;            que.push(u);            v = fail[r];            while (v && !ch[v][c]) v = fail[v];            fail[u] = ch[v][c];            last[u] = val[fail[u]]? fail[u]:last[fail[u]];        }    }}void Print(int x){    if (x){        cnt[val[x]]++;        Print(last[x]);    }}void Find(char *T){   int len = strlen(T);   int j = 0,c;   for (int i=0; i<len; i++){      c = idx(T[i]);      while (j && !ch[j][c]) j = fail[j];      j = ch[j][c];      if (val[j]) Print(j);      else if (last[j]) Print(last[j]);   }}int MAX(int x, int y){return x>y?x:y;}int main(){    while (scanf("%d",&N) && N){        init();        getfail();        scanf("%s",text);        Find(text);        int best = -1;        for (int i=1; i<=N; i++) best = MAX(best,cnt[i]);        printf("%d\n",best);        for (int i=1; i<=N; i++) if (best==cnt[mp[string(p[i])]]) printf("%s\n",p[i]);    }    return 0;}