#include #define ff first #define ss second #define PB push_back using namespace std; using ii = pair; using vi = vector; using ll = long long; const ii N_range = { 1, 1e9 }; const ii Q_range = { 1, 100 }; const int INF = 1e9+7; struct AhoCorasick { struct Vertex { map children; // Links to the child vertexes in the trie: bool active = true; // If word should be found bool leaf = false; // Flag that some word from the dictionary ends in this vertex char parentChar; // Char which moves us from the parent vertex to the current vertex int parent = -1; // Link to the parent vertex int suffixLink = -1; // Suffix link from current vertex (the equivalent of P[i] from the KMP algorithm) int wordId = -1; // If the vertex is the leaf, we store the ID of the word int endWordLink = -1; // Link to the leaf vertex of the maximum-length word we can make from the current prefix }; vector trie = {{}}; vector words; const int root = 0; AhoCorasick(const vector &words) : words(words) { for (size_t i = 0; i < words.size(); ++i) AddString(words[i], i); CreateAhoCorasick(); } void AddString(const string &s, int wordId) { int curVertex = root; for (char c : s) { if (!trie[curVertex].children.count(c)) { trie.push_back({}); trie.back().parent = curVertex; trie.back().parentChar = c; trie[curVertex].children[c] = trie.size() - 1; } curVertex = trie[curVertex].children[c]; // Move to the new vertex in the trie } // Mark the end of the word and store its ID trie[curVertex].leaf = true; trie[curVertex].wordId = wordId; } void CreateAhoCorasick() { queue vertexQueue; vertexQueue.push(root); while (!vertexQueue.empty()) { int curVertex = vertexQueue.front(); vertexQueue.pop(); CalcSuffLink(curVertex); for (auto x : trie[curVertex].children) vertexQueue.push(x.second); } } void CalcSuffLink(int vertexId) { Vertex &vertex = trie[vertexId]; // Processing root (empty string) if (vertexId == root) { vertex.suffixLink = root; vertex.endWordLink = root; return; } // Processing children of the root (one character substrings) if (vertex.parent == root) { vertex.suffixLink = root; vertex.endWordLink = vertex.leaf ? vertexId : trie[vertex.suffixLink].endWordLink; return; } // Cases above are degenerate cases as for prefix function calculation; the // value is always 0 and links to the root vertex. // To calculate the suffix link for the current vertex, we need the suffix // link for the parent of the vertex and the character that moved us to the // current vertex. int curBetterVertex = trie[vertex.parent].suffixLink; char chVertex = vertex.parentChar; // From this vertex and its substring we will start to look for the maximum // prefix for the current vertex and its substring. while (true) { // If there is an edge with the needed char, we update our suffix link // and leave the cycle if (trie[curBetterVertex].children.count(chVertex)) { vertex.suffixLink = trie[curBetterVertex].children[chVertex]; break; } // Otherwise, we are jumping by suffix links until we reach the root // (equivalent of k == 0 in prefix function calculation) or we find a // better prefix for the current substring. if (curBetterVertex == root) { vertex.suffixLink = root; break; } curBetterVertex = trie[curBetterVertex].suffixLink; // Go back by sufflink } // When we complete the calculation of the suffix link for the current // vertex, we should update the link to the end of the maximum length word // that can be produced from the current substring. vertex.endWordLink = vertex.leaf ? vertexId : trie[vertex.suffixLink].endWordLink; } int solve(int N) { vector dp(N+1, vi(trie.size())); dp[0][root] = 1; for (int n = 0; n < N; ++n) { for (int s = 0; s < trie.size(); ++s) { for (char c = 'a'; c <= 'z'; ++c) { // cout << n << " " << s << " " << c << " " << trie[s].leaf << endl; int currentState = s; while (true) { if (trie[currentState].children.count(c)) { currentState = trie[currentState].children[c]; break; } if (currentState == root) break; currentState = trie[currentState].suffixLink; } if (!trie[currentState].leaf) { // cout << currentState << endl; dp[n+1][currentState] = (dp[n+1][currentState] + dp[n][s]) % INF; } } } } int res = 0; for (int s = 0; s < trie.size(); ++s) res = (res + dp[N][s]) % INF; return res; } }; ll modpow(ll b, ll e, ll m) { b %= m; ll r = 1; while (e > 0) { if (e & 1) r = (r * b) % m; b = (b * b) % m; e >>= 1; } return r; } int main() { ios::sync_with_stdio(0); int n, qq; assert(cin >> n && "Could not read N"); assert(N_range.ff <= n && n <= N_range.ss && "Invalid value of N"); assert(cin >> qq && "Could not read Q"); assert(Q_range.ff <= qq && qq <= Q_range.ss && "Invalid value of Q"); vector forbidden(qq); for (int i = 0, lenSum = 0; i < qq; ++i) { int len; assert(cin >> len && "Could not read len"); assert(cin >> forbidden[i] && "Could not read forbidden phrase"); assert(len == (int)forbidden[i].size() && "Size mismatch of forbidden"); lenSum += forbidden[i].size(); assert(lenSum <= Q_range.ss && "Too long forbidden phrases"); } assert(cin.get() == '\n' && cin.get() == EOF && "Garbage after forbidden phrases"); AhoCorasick ac(forbidden); cout << ac.solve(n) << endl; // vector> dp(n+1); // queue> q; // dp[0][""] = 1; // q.push({0,""}); // while (q.size()) { // int d; // string s; // tie(d,s) = q.front(); // q.pop(); // cout << d << " " << s << endl; // if (d == n || !dp[d][s]) // continue; // for (char x = 'a'; x <= 'z'; ++x) { // auto tmp = s + x; // if (tmp.size() > 100) // tmp.substr(1); // if (none_of(forbidden.begin(), forbidden.end(), [tmp] (const string & str) { // return ::strstr(tmp.c_str(), str.c_str()); // })) { // dp[d+1][tmp] = (dp[d+1][tmp] + dp[d][s]) % INF; // q.push({d+1, tmp}); // dp[d][s] = 0; // } // } // } // int res = 0; // for (const auto & x : dp[n]) { // res = (res + x.ss) % INF; // } // cout << (modpow(26, n, INF) - res + INF) % INF << endl; return 0; }