Skip to content

aho_corasick.hpp

SECTIONString INCLUDEnoya/aho_corasick.hpp

把多个模式串建成自动机,一次扫描文本即可找出所有模式出现位置或累计匹配贡献。

Complexity: Time: O(S sig) build and O(n + z) matching, for S states and z matches. Space: O(S sig + z).

AC 记录:aho_corasick

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(S sig) build and O(n + z) matching, for S states and z matches.
/// Space: O(S sig + z).

#include <array>
#include <cassert>
#include <queue>
#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) {
    assert(!ok);
    assert(!t.empty());
    int st = 0;
    for (int c : t) {
      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;
  }

  int add(const std::string &t, char a = 'a') {
    std::vector<int> x;
    x.reserve(t.size());
    for (unsigned char c : t) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return add(x);
  }

  /// @brief Build failure links and total transitions in O(number_of_nodes *
  /// sig).
  void build() {
    assert(!ok);
    ok = true;
    std::queue<int> q;
    ord = {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;
        q.push(v);
      }
    }
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      ord.push_back(u);
      for (int c = 0; c < sig; c++) {
        int v = nd[u].nxt[c];
        if (v == -1) {
          nd[u].nxt[c] = nd[nd[u].lnk].nxt[c];
          continue;
        }
        int lnk = nd[nd[u].lnk].nxt[c];
        nd[v].lnk = lnk;
        nd[v].dl = nd[lnk].out.empty() ? nd[lnk].dl : lnk;
        q.push(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 Enumerate matches as (inclusive end position, pattern id).
  std::vector<std::pair<int, int>> match(const std::vector<int> &s) 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, 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;
  }

  std::vector<std::pair<int, int>> match(const std::string &s,
                                         char a = 'a') const {
    std::vector<int> x;
    x.reserve(s.size());
    for (unsigned char c : s) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return match(x);
  }

  /// @brief Count occurrences of every inserted pattern in O(text + nodes +
  /// patterns).
  std::vector<long long> count_matches(const std::vector<int> &s) const {
    assert(ok);
    std::vector<long long> cnt(nd.size());
    int st = 0;
    for (int c : s) {
      st = transition(st, c);
      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;
  }

  std::vector<long long> count_matches(const std::string &s,
                                       char a = 'a') const {
    std::vector<int> x;
    x.reserve(s.size());
    for (unsigned char c : s) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return count_matches(x);
  }
};

} // namespace noya
#ifndef NOYA_AHO_CORASICK_HPP
#define NOYA_AHO_CORASICK_HPP 1

/// @complexity Time: O(S sig) build and O(n + z) matching, for S states and z matches.
/// Space: O(S sig + z).

#include <array>
#include <cassert>
#include <queue>
#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) {
    assert(!ok);
    assert(!t.empty());
    int st = 0;
    for (int c : t) {
      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;
  }

  int add(const std::string &t, char a = 'a') {
    std::vector<int> x;
    x.reserve(t.size());
    for (unsigned char c : t) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return add(x);
  }

  /// @brief Build failure links and total transitions in O(number_of_nodes *
  /// sig).
  void build() {
    assert(!ok);
    ok = true;
    std::queue<int> q;
    ord = {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;
        q.push(v);
      }
    }
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      ord.push_back(u);
      for (int c = 0; c < sig; c++) {
        int v = nd[u].nxt[c];
        if (v == -1) {
          nd[u].nxt[c] = nd[nd[u].lnk].nxt[c];
          continue;
        }
        int lnk = nd[nd[u].lnk].nxt[c];
        nd[v].lnk = lnk;
        nd[v].dl = nd[lnk].out.empty() ? nd[lnk].dl : lnk;
        q.push(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 Enumerate matches as (inclusive end position, pattern id).
  std::vector<std::pair<int, int>> match(const std::vector<int> &s) 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, 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;
  }

  std::vector<std::pair<int, int>> match(const std::string &s,
                                         char a = 'a') const {
    std::vector<int> x;
    x.reserve(s.size());
    for (unsigned char c : s) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return match(x);
  }

  /// @brief Count occurrences of every inserted pattern in O(text + nodes +
  /// patterns).
  std::vector<long long> count_matches(const std::vector<int> &s) const {
    assert(ok);
    std::vector<long long> cnt(nd.size());
    int st = 0;
    for (int c : s) {
      st = transition(st, c);
      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;
  }

  std::vector<long long> count_matches(const std::string &s,
                                       char a = 'a') const {
    std::vector<int> x;
    x.reserve(s.size());
    for (unsigned char c : s) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return count_matches(x);
  }
};

} // namespace noya

#endif // NOYA_AHO_CORASICK_HPP
#include <array>
#include <cassert>
#include <queue>
#include <string>
#include <utility>
#include <vector>

/// @complexity Time: O(S sig) build and O(n + z) matching, for S states and z matches.
/// Space: O(S sig + z).

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) {
    assert(!ok);
    assert(!t.empty());
    int st = 0;
    for (int c : t) {
      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;
  }

  int add(const std::string &t, char a = 'a') {
    std::vector<int> x;
    x.reserve(t.size());
    for (unsigned char c : t) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return add(x);
  }

  /// @brief Build failure links and total transitions in O(number_of_nodes *
  /// sig).
  void build() {
    assert(!ok);
    ok = true;
    std::queue<int> q;
    ord = {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;
        q.push(v);
      }
    }
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      ord.push_back(u);
      for (int c = 0; c < sig; c++) {
        int v = nd[u].nxt[c];
        if (v == -1) {
          nd[u].nxt[c] = nd[nd[u].lnk].nxt[c];
          continue;
        }
        int lnk = nd[nd[u].lnk].nxt[c];
        nd[v].lnk = lnk;
        nd[v].dl = nd[lnk].out.empty() ? nd[lnk].dl : lnk;
        q.push(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 Enumerate matches as (inclusive end position, pattern id).
  std::vector<std::pair<int, int>> match(const std::vector<int> &s) 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, 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;
  }

  std::vector<std::pair<int, int>> match(const std::string &s,
                                         char a = 'a') const {
    std::vector<int> x;
    x.reserve(s.size());
    for (unsigned char c : s) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return match(x);
  }

  /// @brief Count occurrences of every inserted pattern in O(text + nodes +
  /// patterns).
  std::vector<long long> count_matches(const std::vector<int> &s) const {
    assert(ok);
    std::vector<long long> cnt(nd.size());
    int st = 0;
    for (int c : s) {
      st = transition(st, c);
      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;
  }

  std::vector<long long> count_matches(const std::string &s,
                                       char a = 'a') const {
    std::vector<int> x;
    x.reserve(s.size());
    for (unsigned char c : s) {
      x.push_back(int(c) - int(static_cast<unsigned char>(a)));
    }
    return count_matches(x);
  }
};

} // namespace noya