AC自动机总结


ac自动机是一种处理多模式串匹配的字符串算法


  • 常见应用


  1. 统计有多少个模式串在文本串中出现过, 直接暴力跳$fail$指针,将沿路径上的$end$标记值加上即可,注意为了避免重复匹配,每跳到一个$fail$要将它赋值为$-1$

  2. $fail$树优化 :如计算每个模式串在文本串中出现的次数,当数据范围较小(如 $n$ <= $1e3$,其中$n$为模式串的数量)时,可以选择直接暴力跳$fail$指针;但当数据范围较大时,暴力跳$fail$指针会进行很多重复的操作,从而使复杂度大大增加,这时就可以将所有$fail$指针建一棵树,对于每个模式串,统计以它的字符串结尾单词为根的子树和.

    原理 :

    1. 每一个$fail$结点都只对应一个失配字符,故此可以建树
    2. 短的模式串在树上的深度一定小于长的模式串,故假设当前模式串已匹配,那么比深度小的在同一棵子树上的模式串必然被匹配过一次了,所以以某一个结点为根的子树和为它所对应的字符串被匹配的次数

    代码 :

     struct AC_autumaton {
         int tot;
         int fail[N];
         int end[N];
         int trie[N][26];
         int size_fail[N];
         void insert(char *b, int flag) {
             int p = 0, l = strlen(b + 1);
             for (int i = 1; i <= l; i++) {
                 int x = b[i] - 'a';
                 if (!trie[p][x])
                     trie[p][x] = ++tot;
                 p = trie[p][x];
             } end[flag] = p;
         }
         void build_fail() {
             queue <int> que;
             for (int i = 0; i <= 25; i++)
                 if (trie[0][i]) que.push(trie[0][i]);
             while (!que.empty()) {
                 int p = que.front(); que.pop();
                 for (int i = 0; i <= 25; i++) {
                     if (trie[p][i])
                         fail[trie[p][i]] = trie[fail[p]][i],
                             que.push(trie[p][i]);
                     else trie[p][i] = trie[fail[p]][i];
                 }
             }
         }
         void query(char *a) {
             int p = 0, l = strlen(a + 1);
             for (int i = 1; i <= l; i++) {
                 int x = a[i] - 'a';
                 p = trie[p][x];
                 size_fail[p]++;        //统计结点出现次数
             }
         }
         struct Edge { int nt, to; } ft[N];
         int hd[N], num;
         void add_fail(int u, int v) {
             ft[++num].nt = hd[u];
             ft[num].to = v;
             hd[u] = num;
         }
         void failtree() {
             for (int i = 1; i <= tot; i++)
                 add(fail[i], i);       //按fail指针反向建树
         }
         void dfs_fail(int u) {         //计算子树和
             for (int i = hd[u]; i; i = ft[i].nt) {
                 int v = ft[i].to;
                 dfs_fail(v);
                 size_fail[u] += size_fail[v];
             } return ;
         }
     } ac;  
  3. 将原文本串利用模式串进行操作,如将原文本串中所有的模式串删除,具体参见 -> $luoguP3121$


文章作者: Sagiri
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Sagiri !
  目录