Skip to content

aho_corasick.hpp

SECTIONString INCLUDEnoya/string/aho_corasick.hpp

把多个非空模式串建成稠密 AC 自动机;字符串直接按字节扫描,可按结束位置枚举匹配,或沿失败链累计各模式次数而不展开全部输出。

Complexity: Time: \(O(L + S \mathrm{sig})\) construction, \(O(n + z)\) matching, \(O(n + S + P)\) counting; \(L\) pattern symbols, \(S\) states, \(P\) patterns, \(z\) matches. Space: \(O(S (\mathrm{sig} + 1) + P)\) storage, \(O(z)\) match or \(O(S + P)\) count workspace.

AC 记录:aho_corasick。

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include。

/// @complexity Time: O(L + S sig) construction, O(n + z) matching,
/// O(n + S + P) counting; L pattern symbols, S states, P patterns, z matches.
/// Space: O(S (sig + 1) + P) storage, O(z) match or O(S + P) count workspace.

#include <array>
#include <cassert>
#include <string>
#include <utility>
#include <vector>

namespace noya {

/// @brief Aho-Corasick automaton for an integer alphabet [0, sig).
template <int sig = 26> struct aho_corasick {
  struct node {
    std::array<int, sig> nxt;
    int fa = -1;
    int lnk = 0;
    int dl = -1;
    std::vector<int> out;

    node() { nxt.fill(-1); }
  };

  std::vector<node> nd = {node{}};
  std::vector<int> pat;
  std::vector<int> ord;
  bool ok = false;

  /// @brief Add a nonempty pattern and return its pattern id.
  int add(const std::vector<int> &t) {
    return add_impl(t, [](int c) { return c; });
  }

  int add(const std::string &t, char a = 'a') {
    return add_impl(t, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

  /// @brief Build failure links and total transitions in O(number_of_nodes *
  /// sig).
  void build() {
    assert(!ok);
    ok = true;
    ord.clear();
    ord.reserve(nd.size());
    ord.push_back(0);
    for (int c = 0; c < sig; c++) {
      int v = nd[0].nxt[c];
      if (v == -1) {
        nd[0].nxt[c] = 0;
      } else {
        nd[v].lnk = 0;
        nd[v].dl = -1;
        ord.push_back(v);
      }
    }
    for (int at = 1; at < int(ord.size()); at++) {
      int u = ord[at];
      const auto &fallback = nd[nd[u].lnk].nxt;
      auto children = nd[u].nxt;
      nd[u].nxt = fallback;
      for (int c = 0; c < sig; c++) {
        int v = children[c];
        if (v == -1) continue;
        nd[u].nxt[c] = v;
        int lnk = fallback[c];
        nd[v].lnk = lnk;
        nd[v].dl = nd[lnk].out.empty() ? nd[lnk].dl : lnk;
        ord.push_back(v);
      }
    }
  }

  int transition(int st, int c) const {
    assert(ok);
    assert(0 <= st && st < int(nd.size()));
    assert(0 <= c && c < sig);
    return nd[st].nxt[c];
  }

  /// @brief Matches ordered by end, then decreasing length, then pattern id.
  /// End positions are inclusive. Duplicate patterns retain their own ids.
  std::vector<std::pair<int, int>> match(const std::vector<int> &s) const {
    return match_impl(s, [](int c) { return c; });
  }

  std::vector<std::pair<int, int>> match(const std::string &s,
                                        char a = 'a') const {
    return match_impl(s, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

  /// @brief Count each pattern in O(text + nodes + patterns), without listing.
  std::vector<long long> count_matches(const std::vector<int> &s) const {
    return count_impl(s, [](int c) { return c; });
  }

  std::vector<long long> count_matches(const std::string &s,
                                       char a = 'a') const {
    return count_impl(s, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

private:
  template <class R, class Encode> int add_impl(const R &t, Encode encode) {
    assert(!ok);
    assert(!t.empty());
    int st = 0;
    for (auto ch : t) {
      int c = encode(ch);
      assert(0 <= c && c < sig);
      if (nd[st].nxt[c] == -1) {
        nd[st].nxt[c] = int(nd.size());
        nd.emplace_back();
        nd.back().fa = st;
      }
      st = nd[st].nxt[c];
    }
    int id = int(pat.size());
    pat.push_back(st);
    nd[st].out.push_back(id);
    return id;
  }

  template <class R, class Encode>
  std::vector<std::pair<int, int>> match_impl(const R &s, Encode encode) const {
    assert(ok);
    std::vector<std::pair<int, int>> res;
    int st = 0;
    for (int i = 0; i < int(s.size()); i++) {
      st = transition(st, encode(s[i]));
      for (int u = st; u != -1; u = nd[u].dl) {
        for (int pid : nd[u].out) res.emplace_back(i, pid);
      }
    }
    return res;
  }

  template <class R, class Encode>
  std::vector<long long> count_impl(const R &s, Encode encode) const {
    assert(ok);
    std::vector<long long> cnt(nd.size());
    int st = 0;
    for (auto ch : s) {
      st = transition(st, encode(ch));
      cnt[st]++;
    }
    for (int i = int(ord.size()) - 1; i > 0; i--) {
      int u = ord[i];
      cnt[nd[u].lnk] += cnt[u];
    }
    std::vector<long long> res(pat.size());
    for (int id = 0; id < int(pat.size()); id++) {
      res[id] = cnt[pat[id]];
    }
    return res;
  }
};

} // namespace noya

 // NOYA_AHO_CORASICK_HPP
#ifndef NOYA_AHO_CORASICK_HPP
#define NOYA_AHO_CORASICK_HPP 1

/// @complexity Time: O(L + S sig) construction, O(n + z) matching,
/// O(n + S + P) counting; L pattern symbols, S states, P patterns, z matches.
/// Space: O(S (sig + 1) + P) storage, O(z) match or O(S + P) count workspace.

#include <array>
#include <cassert>
#include <string>
#include <utility>
#include <vector>

namespace noya {

/// @brief Aho-Corasick automaton for an integer alphabet [0, sig).
template <int sig = 26> struct aho_corasick {
  struct node {
    std::array<int, sig> nxt;
    int fa = -1;
    int lnk = 0;
    int dl = -1;
    std::vector<int> out;

    node() { nxt.fill(-1); }
  };

  std::vector<node> nd = {node{}};
  std::vector<int> pat;
  std::vector<int> ord;
  bool ok = false;

  /// @brief Add a nonempty pattern and return its pattern id.
  int add(const std::vector<int> &t) {
    return add_impl(t, [](int c) { return c; });
  }

  int add(const std::string &t, char a = 'a') {
    return add_impl(t, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

  /// @brief Build failure links and total transitions in O(number_of_nodes *
  /// sig).
  void build() {
    assert(!ok);
    ok = true;
    ord.clear();
    ord.reserve(nd.size());
    ord.push_back(0);
    for (int c = 0; c < sig; c++) {
      int v = nd[0].nxt[c];
      if (v == -1) {
        nd[0].nxt[c] = 0;
      } else {
        nd[v].lnk = 0;
        nd[v].dl = -1;
        ord.push_back(v);
      }
    }
    for (int at = 1; at < int(ord.size()); at++) {
      int u = ord[at];
      const auto &fallback = nd[nd[u].lnk].nxt;
      auto children = nd[u].nxt;
      nd[u].nxt = fallback;
      for (int c = 0; c < sig; c++) {
        int v = children[c];
        if (v == -1) continue;
        nd[u].nxt[c] = v;
        int lnk = fallback[c];
        nd[v].lnk = lnk;
        nd[v].dl = nd[lnk].out.empty() ? nd[lnk].dl : lnk;
        ord.push_back(v);
      }
    }
  }

  int transition(int st, int c) const {
    assert(ok);
    assert(0 <= st && st < int(nd.size()));
    assert(0 <= c && c < sig);
    return nd[st].nxt[c];
  }

  /// @brief Matches ordered by end, then decreasing length, then pattern id.
  /// End positions are inclusive. Duplicate patterns retain their own ids.
  std::vector<std::pair<int, int>> match(const std::vector<int> &s) const {
    return match_impl(s, [](int c) { return c; });
  }

  std::vector<std::pair<int, int>> match(const std::string &s,
                                        char a = 'a') const {
    return match_impl(s, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

  /// @brief Count each pattern in O(text + nodes + patterns), without listing.
  std::vector<long long> count_matches(const std::vector<int> &s) const {
    return count_impl(s, [](int c) { return c; });
  }

  std::vector<long long> count_matches(const std::string &s,
                                       char a = 'a') const {
    return count_impl(s, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

private:
  template <class R, class Encode> int add_impl(const R &t, Encode encode) {
    assert(!ok);
    assert(!t.empty());
    int st = 0;
    for (auto ch : t) {
      int c = encode(ch);
      assert(0 <= c && c < sig);
      if (nd[st].nxt[c] == -1) {
        nd[st].nxt[c] = int(nd.size());
        nd.emplace_back();
        nd.back().fa = st;
      }
      st = nd[st].nxt[c];
    }
    int id = int(pat.size());
    pat.push_back(st);
    nd[st].out.push_back(id);
    return id;
  }

  template <class R, class Encode>
  std::vector<std::pair<int, int>> match_impl(const R &s, Encode encode) const {
    assert(ok);
    std::vector<std::pair<int, int>> res;
    int st = 0;
    for (int i = 0; i < int(s.size()); i++) {
      st = transition(st, encode(s[i]));
      for (int u = st; u != -1; u = nd[u].dl) {
        for (int pid : nd[u].out) res.emplace_back(i, pid);
      }
    }
    return res;
  }

  template <class R, class Encode>
  std::vector<long long> count_impl(const R &s, Encode encode) const {
    assert(ok);
    std::vector<long long> cnt(nd.size());
    int st = 0;
    for (auto ch : s) {
      st = transition(st, encode(ch));
      cnt[st]++;
    }
    for (int i = int(ord.size()) - 1; i > 0; i--) {
      int u = ord[i];
      cnt[nd[u].lnk] += cnt[u];
    }
    std::vector<long long> res(pat.size());
    for (int id = 0; id < int(pat.size()); id++) {
      res[id] = cnt[pat[id]];
    }
    return res;
  }
};

} // namespace noya

#endif // NOYA_AHO_CORASICK_HPP
/// @complexity Time: O(L + S sig) construction, O(n + z) matching,
/// O(n + S + P) counting; L pattern symbols, S states, P patterns, z matches.
/// Space: O(S (sig + 1) + P) storage, O(z) match or O(S + P) count workspace.

#include <array>
#include <cassert>
#include <string>
#include <utility>
#include <vector>

namespace noya {

/// @brief Aho-Corasick automaton for an integer alphabet [0, sig).
template <int sig = 26> struct aho_corasick {
  struct node {
    std::array<int, sig> nxt;
    int fa = -1;
    int lnk = 0;
    int dl = -1;
    std::vector<int> out;

    node() { nxt.fill(-1); }
  };

  std::vector<node> nd = {node{}};
  std::vector<int> pat;
  std::vector<int> ord;
  bool ok = false;

  /// @brief Add a nonempty pattern and return its pattern id.
  int add(const std::vector<int> &t) {
    return add_impl(t, [](int c) { return c; });
  }

  int add(const std::string &t, char a = 'a') {
    return add_impl(t, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

  /// @brief Build failure links and total transitions in O(number_of_nodes *
  /// sig).
  void build() {
    assert(!ok);
    ok = true;
    ord.clear();
    ord.reserve(nd.size());
    ord.push_back(0);
    for (int c = 0; c < sig; c++) {
      int v = nd[0].nxt[c];
      if (v == -1) {
        nd[0].nxt[c] = 0;
      } else {
        nd[v].lnk = 0;
        nd[v].dl = -1;
        ord.push_back(v);
      }
    }
    for (int at = 1; at < int(ord.size()); at++) {
      int u = ord[at];
      const auto &fallback = nd[nd[u].lnk].nxt;
      auto children = nd[u].nxt;
      nd[u].nxt = fallback;
      for (int c = 0; c < sig; c++) {
        int v = children[c];
        if (v == -1) continue;
        nd[u].nxt[c] = v;
        int lnk = fallback[c];
        nd[v].lnk = lnk;
        nd[v].dl = nd[lnk].out.empty() ? nd[lnk].dl : lnk;
        ord.push_back(v);
      }
    }
  }

  int transition(int st, int c) const {
    assert(ok);
    assert(0 <= st && st < int(nd.size()));
    assert(0 <= c && c < sig);
    return nd[st].nxt[c];
  }

  /// @brief Matches ordered by end, then decreasing length, then pattern id.
  /// End positions are inclusive. Duplicate patterns retain their own ids.
  std::vector<std::pair<int, int>> match(const std::vector<int> &s) const {
    return match_impl(s, [](int c) { return c; });
  }

  std::vector<std::pair<int, int>> match(const std::string &s,
                                        char a = 'a') const {
    return match_impl(s, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

  /// @brief Count each pattern in O(text + nodes + patterns), without listing.
  std::vector<long long> count_matches(const std::vector<int> &s) const {
    return count_impl(s, [](int c) { return c; });
  }

  std::vector<long long> count_matches(const std::string &s,
                                       char a = 'a') const {
    return count_impl(s, [a](unsigned char c) {
      return int(c) - int(static_cast<unsigned char>(a));
    });
  }

private:
  template <class R, class Encode> int add_impl(const R &t, Encode encode) {
    assert(!ok);
    assert(!t.empty());
    int st = 0;
    for (auto ch : t) {
      int c = encode(ch);
      assert(0 <= c && c < sig);
      if (nd[st].nxt[c] == -1) {
        nd[st].nxt[c] = int(nd.size());
        nd.emplace_back();
        nd.back().fa = st;
      }
      st = nd[st].nxt[c];
    }
    int id = int(pat.size());
    pat.push_back(st);
    nd[st].out.push_back(id);
    return id;
  }

  template <class R, class Encode>
  std::vector<std::pair<int, int>> match_impl(const R &s, Encode encode) const {
    assert(ok);
    std::vector<std::pair<int, int>> res;
    int st = 0;
    for (int i = 0; i < int(s.size()); i++) {
      st = transition(st, encode(s[i]));
      for (int u = st; u != -1; u = nd[u].dl) {
        for (int pid : nd[u].out) res.emplace_back(i, pid);
      }
    }
    return res;
  }

  template <class R, class Encode>
  std::vector<long long> count_impl(const R &s, Encode encode) const {
    assert(ok);
    std::vector<long long> cnt(nd.size());
    int st = 0;
    for (auto ch : s) {
      st = transition(st, encode(ch));
      cnt[st]++;
    }
    for (int i = int(ord.size()) - 1; i > 0; i--) {
      int u = ord[i];
      cnt[nd[u].lnk] += cnt[u];
    }
    std::vector<long long> res(pat.size());
    for (int id = 0; id < int(pat.size()); id++) {
      res[id] = cnt[pat[id]];
    }
    return res;
  }
};

} // namespace noya

 // NOYA_AHO_CORASICK_HPP