#include "bits/stdc++.h" using namespace std; using ll = long long; #define REP(i, n) for (int i = 0; i < n; ++i) #define ALL(v) v.begin(), v.end() using namespace std; // 入力文字列に対してマッチするパターンを検索 // O(N + M) // N := 入力文字列, M := パターン文字列の長さ合計 /* 1. パターンから Trie 木を作成. 2. 各ノードが表す文字列の末尾と一致するノードの内, 文字列長が最大のノードへ辺を張る. そのようなノードが存在しなければ根に辺を張る. この処理は bfs で可能. 各ノードはその祖先が一致する文字列の情報も持つ. 3. PMA(パターンマッチングオートマトン)の完成! */ // 出現文字 struct characters { // lower alphabets static int const size = 26; static int convert(char c) { return c - 'A'; } static char invert(int i) { return i + 'A'; } }; template struct AhoCorasick { static constexpr int invalid = -1; struct PMA { int fail; vector next, accept; PMA() : fail(invalid), next(Char::size, invalid) {} }; const int K; vector> nodes; int next(int index, char cc) { int c = Char::convert(cc); int now = index; while (nodes[now]->next[c] == invalid && now != 0) { now = nodes[now]->fail; } now = nodes[now]->next[c]; if (now == invalid) now = 0; return now; } AhoCorasick(const vector &ts) : K((int)ts.size()) { const int root = 0; // root node nodes.push_back(make_unique()); nodes[root]->fail = root; for (int i = 0; i < K; i++) { int now = root; for (auto cs : ts[i]) { int c = Char::convert(cs); if (nodes[now]->next[c] == invalid) { nodes[now]->next[c] = (int)nodes.size(); nodes.push_back(make_unique()); } now = nodes[now]->next[c]; } nodes[now]->accept.push_back(i); } queue que; for (int c = 0; c < Char::size; c++) { if (nodes[root]->next[c] != invalid) { nodes[nodes[root]->next[c]]->fail = root; que.push(nodes[root]->next[c]); } } while (!que.empty()) { int now = que.front(); que.pop(); for (int c = 0; c < Char::size; c++) { if (nodes[now]->next[c] != invalid) { que.push(nodes[now]->next[c]); int nxt = next(nodes[now]->fail, Char::invert(c)); nodes[nodes[now]->next[c]]->fail = nxt; for (auto ac : nodes[nxt]->accept) { nodes[nodes[now]->next[c]]->accept.push_back(ac); } } } } // 最悪O(1)にする部分 for (int c = 0; c < Char::size; c++) { if (nodes[root]->next[c] != invalid) { que.push(nodes[root]->next[c]); } else { nodes[root]->next[c] = root; } } while (!que.empty()) { int p = que.front(); que.pop(); for (int c = 0; c < Char::size; c++) { if (nodes[p]->next[c] == invalid) { int nxt = nodes[nodes[p]->fail]->next[c]; assert(nxt != invalid); nodes[p]->next[c] = nxt; } else { que.push(nodes[p]->next[c]); } } } // ここまで } vector> match(const string &str) { vector> ret(K); int now = 0; for (int i = 0; i < (int)str.size(); i++) { now = next(now, str[i]); for (auto k : nodes[now]->accept) { ret[k].push_back(i); } } return ret; } }; int main() { string s; cin >> s; int m; cin >> m; vector v(m); REP(i, m) cin >> v[i]; AhoCorasick aho(v); ll ans = 0; auto res = aho.match(s); for (auto a : res) ans += a.size(); cout << ans << endl; }