LUOGU3976 AC自动机

LUOGU3808
LUOGU3976
Description
有个由小写字母组成的模式串以及一个文本串。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串中出现的次数最多。
Input Format
输入含多组数据。
每组数据的第一行为一个正整数,表示共有个模式串,。
接下去行,每行一个长度小于等于的模式串。下一行是一个长度小于等于的文本串。
输入结束标志为。

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

Sample Input

2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0

Sample Output

4
aba
2
alpha
haha

Constraints
见题面。

CCYOS
这是一道AC自动机的板子题。

AC自动机 可以理解为在Trie图上的KMP算法。

LUOGU3976 AC自动机_第1张图片
这是一棵trie树

对于正常的遍历,每次在这棵树上搜索的时候会因为回溯而浪费大量时间。因此我们考虑对它进行优化,直接建立“树边”:搜索时,在当前节点的儿子中下一个字符失配时,能够快速地跳转到下一个可以匹配下一个字符的节点,也就是当前可匹配的最长后缀。
设当前节点为 ,则当前节点的“树边”指向 。
LUOGU3976 AC自动机_第2张图片
建立nxt数组

举一个更详细的例子:
LUOGU3976 AC自动机_第3张图片
详细的例子

对文本串 ader 进行匹配时,发现在匹配至 8号节点时失配,于是直接跳转到 10号节点继续进行匹配。
如何建立nxt数组 - get_nxt
根节点的后继是0,根节点的直接下属结点的后继也是0。(重新开始匹配)
余下的结点的后继:设到节点 上的边的字符为 ,父亲结点为 ,则 。

code

inline void get_nxt(){
  int now = 0;
  queue q;while(q.size())q.pop();
  for(int i = 0;i < 26;++i)if(ch[0][i])q.push(ch[0][i]);
  while(q.size()){
    now = q.front();q.pop();
    for(int i = 0;i < 26;++i){
      if(ch[now][i])q.push(ch[now][i]),nxt[ch[now][i]] = ch[nxt[now]][i];
      else ch[now][i] = ch[nxt[now]][i];
    }
  }
}

trie树的建立
先沿着树搜索,在空点处加点,标记最后一个字符声明这是一个字符串的结尾。记树的节点数为。

code

int cnt;
inline void build(char* s){
  int now = 0,len = strlen(s);
  for(int i = 0;i < len;++i){
    int c = s[i] - 'a';
    if(!ch[now][c])ch[now][c] = ++cnt;
    now = ch[now][c];
  }
  ed[now] = 1;
}

trie树的搜索
计数式:沿着树搜索,向下一层,搜索后继结点计数。
bool式:勇就完事了。

code

//计数式
inline void ask(char* s){
  int now = 0,len = strlen(s);
  for(int i = 0;i < len;++i){
    now = ch[now][s[i] - 'a'];
    for(int t = now;t;t = nxt[t])ans[ed[t]]++;
  }
}
//bool式
inline bool ask(char* s){
  int len = strlen(s),now = 0;
  now = ch[now][s[i] - 'a'];
  if(end[now])return 1;
  }
  return 0;
}

多组数据一定要清空全部数组。
code

#include
using namespace std;

int N,tot;
int ch[1000005][30],nxt[1000005],endd[1000005],cnt[155];
struct str{
    char a[75];
}inn[155];
char in[1000005];
struct Ans{
    int num,id;
}ans[155];
inline bool cmp(Ans a,Ans b){
    if(a.num == b.num)return a.id < b.id;
    return a.num > b.num;
}

inline void build(char* s,int id){
    int len = strlen(s);int now = 0;
    for(int i = 0;i < len;++i){
        int c = s[i] - 'a';
        if(!ch[now][c])ch[now][c] = ++tot;
        now = ch[now][c];
    }
    endd[now] = id;
}

inline void get_nxt(){
    queue q;while(q.size())q.pop();
    for(int i = 0;i < 26;++i)if(ch[0][i])q.push(ch[0][i]);
    int now = 0;
    while(q.size()){
        now = q.front();q.pop();
        for(int i = 0;i < 26;++i){
            if(!ch[now][i])ch[now][i] = ch[nxt[now]][i];
            else nxt[ch[now][i]] = ch[nxt[now]][i],q.push(ch[now][i]);
        }
    }
}

inline void ask(char* s){
    int len = strlen(s),now = 0;
    for(int i = 0;i < len;++i){
        int c = s[i] - 'a';
        now = ch[now][c];
        for(int t = now;t;t = nxt[t])++ans[endd[t]].num;
    }
    sort(ans + 1,ans + 155,cmp);
    int num = ans[1].num;
    printf("%d\n",num);
    for(int i = 1;i <= 150&&ans[i].num == num;++i)printf("%s\n",inn[ans[i].id].a);
}

int main(){
    freopen("3796-1.in","r",stdin);
    scanf("%d",&N);
    while(N){
        tot = 0;
        for(int i = 1;i <= N;++i){
            scanf("%s",inn[i].a);
            ans[i].id = i,ans[i].num = 0;
            build(inn[i].a,i);
        }
        get_nxt();
    //  cout<<"                                             over"<

你可能感兴趣的:(LUOGU3976 AC自动机)