zoukankan      html  css  js  c++  java
  • P3796 AC自动机(加强版) [模板]

    AC()AC自动机(加强版)

    有 N 个由小写字母组成的模式串以及一个文本串 T 。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 T 中出现的次数最多。
    每组数据的第一行为一个正整数 N ,表示共有 N 个模式串, 1 ≤ N ≤ 150 。
    接下去 N 行,每行一个长度小于等于 70 的模式串。下一行是一个长度小于等于 10^6 的文本串 T 。
    输入结束标志为 N=0
    对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。


    color{red}{正解部分}

    以此题为例说一下 ACAC 自动机的相关内容 .

    1.1. 建立自动机
    • 首先将所有的 模式串 加入 TrieTrie树 .
    • BFSBFS序 寻找 failfail指针,
      设当前 BFSBFS 到了 TrieTrie树 中的 ii 节点, 代表了字符串 SS, 结尾字符为 cc, 其父节点为 fa[i]fa[i],
      cc 不存在, ccfailfail指针 指向的是 TrieTrie树 中除了 SS 以外存在的字符串中与 SS 有最长公共后缀的字符串对应的 TrieTrie树节点 jj .
      于是 ccfail[i]fail[i]就可以表示为: fail[i]=ch[fail[fa[i]]][c]fail[i] = ch[fail[fa[i]]][c] .
    • fail[i]fail[i] 连向 终止节点, 则该点为 “伪终止节点”, 表示到了ii节点, 等同到了一个终止节点, 需要计数 .
    • 为了使下方匹配更加迅速, 记录 ii 节点不断跳 failfail 遇到的第一个 终止节点last[i]last[i],
      : last[i]=end[fail[i]]?fail[i]:last[fail[i]]更新方法: last[i] = end[fail[i]]?fail[i]:last[fail[i]] .
    2.2. 匹配字符串

    文本串 起始位置, TrieTrie树根节点开始匹配, 到ii点的时候,
    统计ii点能连到的所有 终止节点 即可, 如下 .

    int tmp = i;
    while(i){
    		统计i点答案;
    		i = last[i];
    }
    

    color{red}{实现部分}

    #include<bits/stdc++.h>
    #define reg register
    
    const int maxn = 1e6 + 10;
    
    int N;
    
    char T[maxn];
    char s[155][75];
    
    struct Asw{ int cnt, id; } Ans[maxn];
    
    bool cmp(Asw a, Asw b){ return a.cnt==b.cnt?a.id<b.id:a.cnt > b.cnt; }
    
    struct Ac_auto{
            int node_cnt;
            int end[20004];
            struct Trie{ int vis[30], nxt, is_end, last;} node[20004];
            void Init(){
                    node_cnt = 0;
                    memset(end, 0, sizeof end);
                    for(reg int i = 0; i < 20004; i ++)
                            memset(node[i].vis, 0, sizeof node[i].vis),
                            node[i].nxt = node[i].is_end = node[i].last = 0;
            }
            void Add(char *s, int id){
                    int cur = 0, size = strlen(s);
                    for(reg int i = 0; i < size; i ++){
                            int t = s[i] - 'a';
                            if(!node[cur].vis[t]) node[cur].vis[t] = ++ node_cnt;
                            cur = node[cur].vis[t];
                    }
                    node[cur].is_end ++, end[cur] = id;
            }
            void BFS(){
                    std::queue <int> Q;
                    for(reg int i = 0; i < 26; i ++)
                            if(node[0].vis[i]) Q.push(node[0].vis[i]); 
                    while(!Q.empty()){
                            int ft = Q.front(); Q.pop();
                            node[ft].last = node[node[ft].nxt].is_end?node[ft].nxt:node[node[ft].nxt].last;
                            for(reg int i = 0; i < 26; i ++)
                                    if(node[ft].vis[i]){
                                            node[node[ft].vis[i]].nxt = node[node[ft].nxt].vis[i];
                                            Q.push(node[ft].vis[i]);
                                    }
                                    else node[ft].vis[i] = node[node[ft].nxt].vis[i];
                    }
            }
            void Find(char *T){
                    int cur = 0, size = strlen(T);
                    for(reg int i = 0; i < size; i ++){
                            int t = T[i] - 'a';
                            cur = node[cur].vis[t]; 
                            int tmp = cur; 
                            while(tmp){ 
                                    if(node[tmp].is_end) Ans[end[tmp]].cnt += node[tmp].is_end; 
                                    tmp = node[tmp].last;
                            }
                    }
            }
    } Ac_t;
    
    void Work(){
            Ac_t.Init();
            for(reg int i = 1; i <= N; i ++) Ans[i].cnt = 0, Ans[i].id = i;
            for(reg int i = 1; i <= N; i ++) scanf("%s", s[i]), Ac_t.Add(s[i], i);
            Ac_t.BFS();
            scanf("%s", T); 
            Ac_t.Find(T);
            std::sort(Ans+1, Ans+N+1, cmp);
            int t = 1;
            printf("%d
    ", Ans[1].cnt);
            printf("%s
    ", s[Ans[1].id]);
            while(t < N && Ans[t].cnt == Ans[t+1].cnt) printf("%s
    ", s[Ans[++ t].id]);
    }
    
    int main(){
            while(~scanf("%d", &N) && N) Work();
            return 0;
    }
    
  • 相关阅读:
    [转]asp.net页面缓存技术
    UL和LI在div中的高度的IE6下兼容性
    jquery制作的横向图片滚动带横向滚动条TackerScroll
    电脑可以上网,但是qq登陆不上去?
    Introduction to discrete event system学习笔记4.6
    Introduction to Discrete event system学习笔记4.9
    Introduction to discrete event systemsstudy 4.5
    Symbolic synthesis of obserability requirements for diagnosability B.Bittner,M.Bozzano,A.Cimatti,and X.Olive笔记4.16
    Introduction to discrete event system学习笔记4.8pm
    Introduction to discrete event system学习笔记 4.8
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822512.html
Copyright © 2011-2022 走看看