/** * CERC 2018 * Problem Solution: The ABCD Murderer * Idea: Use Aho-Corasick to match words and greedily find the solution * @author Roman Jelinek */ #include using namespace std; using ll = long long; using ii = pair; using vi = vector; using vii = vector; using vvi = vector; #define ALL(x) (x).begin(),(x).end() #define ff first #define ss second #define D(a) cerr << ">> " << #a << " = >" << a << "<" << endl #define PB push_back #define F(i,a,b) for ( int i = (int)(a); i < (int)(b); ++i ) #define FD(i,a,b) for ( int i = int(a); i >= int(b); --i ) #define R1(a) scanf("%d",&a) #define R2(a,b) scanf("%d%d",&a,&b) #define R3(a,b,c) scanf("%d%d%d",&a,&b,&c) #define DR1(a) int a; scanf("%d",&a) #define DR2(a,b) int a,b; scanf("%d%d",&a,&b) #define DR3(a,b,c) int a,b,c; scanf("%d%d%d",&a,&b,&c) const int INF = 1e9 + 7; constexpr const double PI = acos(-1); const double EPS = 1e-9; namespace Strings { struct AhoCorasick { struct Vertex { map children; bool active = true; bool leaf = false; char parentChar; int parent = -1; int suffixLink = -1; int wordId = -1; int endWordLink = -1; int longestMatch = -1; }; 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(); } int FindLongestMatch(int currentState) { if (trie[currentState].longestMatch == -1) { int checkState = trie[currentState].endWordLink; if (checkState == root) return 0; int matchLength = words[trie[checkState].wordId].size(); checkState = trie[checkState].suffixLink; trie[currentState].longestMatch = max(matchLength, FindLongestMatch(checkState)); } return trie[currentState].longestMatch; } 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]; } 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]; if (vertexId == root) { vertex.suffixLink = root; vertex.endWordLink = root; return; } if (vertex.parent == root) { vertex.suffixLink = root; vertex.endWordLink = vertex.leaf ? vertexId : trie[vertex.suffixLink].endWordLink; return; } int curBetterVertex = trie[vertex.parent].suffixLink; char chVertex = vertex.parentChar; while (true) { if (trie[curBetterVertex].children.count(chVertex)) { vertex.suffixLink = trie[curBetterVertex].children[chVertex]; break; } if (curBetterVertex == root) { vertex.suffixLink = root; break; } curBetterVertex = trie[curBetterVertex].suffixLink; } vertex.endWordLink = vertex.leaf ? vertexId : trie[vertex.suffixLink].endWordLink; } vector> ProcessString(const string &text, vi & matchTill) { int currentState = root; vector> result; for (size_t j = 0; j < text.size(); j++) { while (true) { if (trie[currentState].children.count(text[j])) { currentState = trie[currentState].children[text[j]]; break; } if (currentState == root) break; currentState = trie[currentState].suffixLink; } int longestMatch = FindLongestMatch(currentState); if (longestMatch) matchTill[j + 1 - longestMatch] = max( matchTill[j + 1 - longestMatch], (int)j + 1 + 1); } return result; } }; } // reading input void R() { } template void R(T & x, Args & ... args) { cin >> x; R(args...); } int main( ) { ios::sync_with_stdio(0); string str; int n; R(n, str); vector words(n); vi matchTill(str.size()+1, 0); for (auto & word : words) R(word); Strings::AhoCorasick ac(words); ac.ProcessString(str, matchTill); int res = 0, matched = 0, matchingTill = 1; while (matchingTill <= (int)str.size()) { ++res; int matchingTillBackup = matchingTill; for (; matched < matchingTillBackup; ++matched) matchingTill = max(matchingTill, matchTill[matched]); if (matchingTill == matchingTillBackup) { printf("-1\n"); return 0; } } printf("%d\n", res); return 0; }