P3796 AC自动机(加强版) [模板]

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

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


正 解 部 分 \color{red}{正解部分}

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

1. 建 立 自 动 机 1. 建立自动机 1.
  • 首先将所有的 模式串 加入 T r i e Trie Trie树 .
  • B F S BFS BFS序 寻找 f a i l fail fail指针,
    设当前 B F S BFS BFS 到了 T r i e Trie Trie树 中的 i i i 节点, 代表了字符串 S S S, 结尾字符为 c c c, 其父节点为 f a [ i ] fa[i] fa[i],
    c c c 不存在, c c c f a i l fail fail指针 指向的是 T r i e Trie Trie树 中除了 S S S 以外存在的字符串中与 S S S 有最长公共后缀的字符串对应的 T r i e Trie Trie树节点 j j j .
    于是 c c c f a i l [ i ] fail[i] fail[i]就可以表示为: f a i l [ i ] = c h [ f a i l [ f a [ i ] ] ] [ c ] fail[i] = ch[fail[fa[i]]][c] fail[i]=ch[fail[fa[i]]][c] .
  • f a i l [ i ] fail[i] fail[i] 连向 终止节点, 则该点为 “伪终止节点”, 表示到了 i i i节点, 等同到了一个终止节点, 需要计数 .
  • 为了使下方匹配更加迅速, 记录 i i i 节点不断跳 f a i l fail fail 遇到的第一个 终止节点 l a s t [ i ] last[i] last[i],
    更 新 方 法 :   l a s t [ i ] = e n d [ f a i l [ i ] ] ? f a i l [ i ] : l a s t [ f a i l [ i ] ] 更新方法:\ last[i] = end[fail[i]]?fail[i]:last[fail[i]] : last[i]=end[fail[i]]?fail[i]:last[fail[i]] .
2. 匹 配 字 符 串 2. 匹配字符串 2.

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

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\n", Ans[1].cnt);
        printf("%s\n", s[Ans[1].id]);
        while(t < N && Ans[t].cnt == Ans[t+1].cnt) printf("%s\n", s[Ans[++ t].id]);
}

int main(){
        while(~scanf("%d", &N) && N) Work();
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值