#pragma GCC target("avx2,avx") #pragma GCC optimize("O3") #pragma GCC optimize("unroll-loops") #include using namespace std; using ll = long long; using ull = unsigned long long; using pii = pair; using pll = pair; #define rep(i, n) for(int i = 0; i < (n); ++i) #define all(x) (x).begin(),(x).end() #define SZ(x) ((int)(x).size()) constexpr char ln = '\n'; template inline bool chmax(T1 &a, T2 b) {if (a < b) {a = b; return true;} return false;} template inline bool chmin(T1 &a, T2 b) {if (a > b) {a = b; return true;} return false;} struct Fast_ios {Fast_ios() {cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(20);};} fast_ios; ///////////////////////////////////////////////////////////////////////////////////////////// template struct SuffixAutomaton { //各nodeは複数の部分文字列を担当する //最長の文字列の長さはnode.len //最短の文字列の長さはnode.link.len+1 struct State { int len, link, original; map nex; State(int len, int link, int original) : len(len), link(link), original(original) {} }; vector node; vector tid; int last; SuffixAutomaton() : last(0) {node.emplace_back(0,-1,0);} //文字をcurの直後に追加する //各文字に紐づいた値でdpしたいときはdp[last] += val する int add(C c, int cur = -1) { if (cur == -1) cur = last; if (node[cur].nex.count(c) && node[node[cur].nex[c]].len == node[cur].len + 1) { int q = node[cur].nex[c]; return last = q; } int p = cur; int nex = node.size(); node.emplace_back(node[p].len+1,0,nex); while (p != -1 && !node[p].nex.count(c)) { node[p].nex[c] = nex; p = node[p].link; } if (p == -1) { node[nex].link = 0; return last = nex; } int q = node[p].nex[c]; if (node[p].len+1 == node[q].len) { node[nex].link = q; } else { int clone = node.size(); node.emplace_back(node[q]); node[clone].len = node[p].len + 1; while (p != -1 && node[p].nex[c] == q) { node[p].nex[c] = clone; p = node[p].link; } node[q].link = node[nex].link = clone; } return last = nex; } void sortTopologically() { vector deg(node.size()); for(int i=0; i < (int)node.size(); i++) { for(auto &p : node[i].nex) deg[p.second]++; } queue que; que.emplace(0); while(!que.empty()) { int v = que.front(); que.pop(); tid.emplace_back(v); for(auto &p : node[v].nex) { if(--deg[p.second] == 0) que.emplace(p.second); } } } vector buildSuffixTree() { vector ret; vector> G(node.size()); for(int i = 1; i < (int)node.size(); i++) G[node[i].link].emplace_back(i); queue que; que.emplace(0); while(!que.empty()) { int v = que.front(); que.pop(); ret.emplace_back(v); for(auto nv : G[v]) que.push(nv); } return ret; } //相異なる部分文字列の個数 long long numberOfDistinctSubstrings() { assert(!tid.empty()); vector dp(node.size()); dp[0] = 1; long long ret = 0; for (int i = 0; i < (int)node.size(); ++i) { int u = tid[i]; ret += dp[u]; for (auto &j : node[u].nex) { dp[j.second] += dp[u]; } } return ret-1; } //SAにある部分文字列が何回出現するか調べられるdpテーブルを返す //根から辿った先の添え字の値が出現する回数 vector enumNumberOfOccurrences() { auto vid = buildSuffixTree(); vector dp(node.size()); for (int i = (int)node.size()-1; i > 0; --i) { int u = vid[i]; if (node[u].original==u) dp[u]++; dp[node[u].link] += dp[u]; } return dp; } //部分文字列の初回出現位置 [l,r)のrを返す vector enumFirstOccurences() { assert(!tid.empty()); vector ret(node.size()); for (int i = (int)node.size()-1; i >= 0; --i) { ret[tid[i]] = node[node[tid[i]].original].len; } return ret; } //部分文字列の最終出現位置 [l,r)のrを返す vector enumLastOccurrences() { assert(!tid.empty()); vector ret(node.size()); for (int i = (int)node.size()-1; i >= 0; --i) { int u = tid[i]; ret[u] = max(ret[u],node[u].len); if (i > 0) ret[node[u].link] = max(ret[node[u].link],ret[u]); } return ret; } State operator[](int i) const {return node[i];} }; //////////////////////////////////////////////////////////////////////////////////// void yosupo_judge() { string S; cin >> S; SuffixAutomaton SA; for (int i = 0, cur = -1; i < SZ(S); i++) { cur = SA.add(S[i],cur); } SA.sortTopologically(); cout << SA.numberOfDistinctSubstrings() << ln; } void yuki430() { string S; cin >> S; SuffixAutomaton SA; for (int i = 0, cur = 0; i < SZ(S); i++) { cur = SA.add(S[i],cur); } int Q; cin >> Q; auto dp = SA.enumNumberOfOccurrences(); ll ans = 0; while (Q--) { string T; cin >> T; int cur = 0; for (int i = 0; i < SZ(T); i++) { if (!SA[cur].nex.count(T[i])) { cur = -1; break; } cur = SA[cur].nex[T[i]]; } if (~cur) ans += dp[cur]; } cout << ans << ln; } int main() { //yosupo_judge(); yuki430(); } /* verified on 2020/09/03 https://judge.yosupo.jp/problem/number_of_substrings */