#line 1 "test.cpp" // competitive-verifier: PROBLEM https://yukicoder.me/problems/no/430 #include using namespace std; #line 7 "/home/hidehic0/src/github.com/hidehic0/library_cpp/templates/alias.hpp" template using VC = std::vector; template using rpriority_queue = std::priority_queue, std::greater>; using ll = long long; using ld = long double; using pii = std::pair; using vi = VC; using vvi = VC; using vvvi = VC; using vb = VC; using vvb = VC; using vf = VC; using vvf = VC; using vpii = VC; using vvpii = VC; using si = std::set; using spii = std::set; using mii = std::map; const std::string upperlist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const std::string lowerlist = "abcdefghijklmnopqrstuvwxyz"; #define mp make_pair #define dms << " " << constexpr int MOD998 = 998244353; #line 4 "/home/hidehic0/src/github.com/hidehic0/library_cpp/templates/macro.hpp" // 引数の長さで内容が変わるrep 参考: https://trap.jp/post/1224 #define overload4(a, b, c, d, ...) d #define _rep(i, n) for (int i = 0; i < (int)(n); i++) #define REP(i, a, b) for (int i = (int)(a); i < (int)(b); ++i) #define rep(...) overload4(__VA_ARGS__, REP, _rep)(__VA_ARGS__) #define _rrep(i, n) for (int i = n - 1; i >= 0; i--) #define RREP(i, a, b) for (int i = (int)(b - 1); i >= (int)(a); i--) #define rrep(...) overload4(__VA_ARGS__, RREP, _rrep)(__VA_ARGS__) #define all(a) (a).begin(), (a).end() template bool chmin(T &a, T b) { if (a > b) { a = b; return true; } return false; } template bool chmax(T &a, T b) { if (a < b) { a = b; return true; } return false; } template std::istream &operator>>(std::istream &is, std::pair &p) { is >> p.first >> p.second; return is; } template std::istream &operator>>(std::istream &is, std::vector &v) { for (T &in : v) is >> in; return is; } template std::ostream &operator<<(std::ostream &os, const std::vector &v) { for (int i = 0; i < (int)v.size(); i++) { os << v[i] << (i + 1 == v.size() ? "" : " "); } return os; } // pythonのprintライクな関数 参考: // https://nyaannyaan.github.io/library/template/inout.hpp inline void out() { std::cout << std::endl; } template void out(const T &t, const U &...u) { std::cout << t; if (sizeof...(u)) std::cout << sep; out(u...); } // cinの短縮関数 参考: https://nyaannyaan.github.io/library/template/inout.hpp inline void in() {} template void in(T &t, U &...u) { std::cin >> t; in(u...); } template inline T ceil_div(T a, T b) { return (a + b - 1) / b; } template inline T mod_pow(T a, T n, T mod) { T res = 1; while (n) { if (n % 2 != 0) { res *= a; res %= mod; } a *= a; a %= mod; n >>= 1; } return res; } template inline T minus_mod(T a, T b) { return ((a % b) + b) % b; } template void apply_vec(std::vector &v, T (*fn)(T)) { for (int i = 0; i < v.size(); i++) v[i] = fn(v[i]); } #line 7 "test.cpp" #line 3 "/home/hidehic0/src/github.com/hidehic0/library_cpp/string/trie.hpp" /*! * @struct Trie * Trie木構造体 使う文字は[margin,margin+char_size)の範囲でないといけない * @note * 参考: https://nyaannyaan.github.io/library/string/trie.hpp */ template struct Trie { struct Node { std::array nxt; std::vector accepts; char key; int child_accepts = 0; Node(char c) : key(c) { fill(nxt.begin(), nxt.end(), -1); }; }; std::vector nodes; Trie(char root = '$') { nodes.emplace_back(root); } inline int get_next(int i, int j) { return nodes[i].nxt[j]; } void add(std::string S, int id = -1) { int cur = 0; for (char s : S) { int k = s - margin; assert(0 <= k && k < char_size); if (~get_next(cur, k)) { cur = get_next(cur, k); continue; } int nxt = nodes.size(); nodes[cur].nxt[k] = nxt; nodes.emplace_back(s); nodes[nxt].child_accepts++; cur = nxt; } nodes[cur].accepts.emplace_back(id == -1 ? nodes[0].child_accepts++ : id); } int size() { return nodes.size(); } }; /*! * @file string/trie.hpp * @brief trie木のstructが置いてある * @auther hidehic0 */ #line 5 "/home/hidehic0/src/github.com/hidehic0/library_cpp/string/aho-corasick.hpp" /*! * @struct AhoCorasick * string/trie.hppのTrie構造体を継承している * trieの各ノードに対して、存在しない子ノードから、子を連結した文字列のprefixの中で長さが最大のノードに繋ぐオートマトンを作成する * テンプレート引数のheavyをtrueにすると、各ノードに対してマッチする文字列の列を管理できる * falseにすると個数のみ * build関数でオートマトンを作成する O(S) */ template struct AhoCorasick : Trie { using TRIE = Trie; using TRIE::get_next; std::vector correct; /*! * @fn * trieの各ノードに対して、存在しない子ノードから、子を連結した文字列のprefixの中で長さが最大のノードに繋ぐオートマトンを作成する * 計算量は追加された文字列の長さの和をSとしてO(S) */ void build() { correct.resize(this->nodes.size()); for (int i = 0; i < this->size(); i++) { correct[i] = this->nodes[i].accepts.size(); } std::queue que; for (int k = 0; k < char_size; k++) { if (~get_next(0, k)) { this->nodes[get_next(0, k)].nxt[char_size] = 0; que.emplace(get_next(0, k)); } else { this->nodes[0].nxt[k] = 0; } } while (!que.empty()) { auto &x = this->nodes[que.front()]; int fail = x.nxt[char_size]; correct[que.front()] += correct[fail]; que.pop(); for (int k = 0; k < char_size; k++) { int &nxt = x.nxt[k]; if (nxt < 0) { nxt = get_next(fail, k); continue; } que.emplace(nxt); this->nodes[nxt].nxt[char_size] = get_next(fail, k); if (heavy) { auto &accept_x = this->nodes[nxt].accepts; auto &accept_y = this->nodes[get_next(fail, k)].accepts; std::vector accept; set_union(accept_x.begin(), accept_x.end(), accept_y.begin(), accept_y.end(), std::back_inserter(accept)); accept_x = accept; } } } } std::pair move(const char &c, int now = 0) { now = get_next(now, c - margin); return {correct[now], now}; } std::pair move(const std::string &s, int now = 0) { int res = 0; for (auto &c : s) { auto [cnt, nxt] = move(c, now); res += cnt, now = nxt; } return {res, now}; } }; /*! * @file string/aho-corasick.hpp * @brief AhoCorasick構造体が置いてある * @auther hidehic0 * @date 2026-05-19 */ #line 9 "test.cpp" int main() { string S; ll N; in(S, N); AhoCorasick<26, 'A'> AH; rep(i, N) { string T; in(T); AH.add(T, i); } AH.build(); cout << AH.move(S, 0).first << "\n"; }