/* The fast and intended solution to "Flipping Out". The solution is easier to understand if you first understand the slow solution. Essentially, we need to know when a pattern breaks a rule. When a mismatch occurs, the only thing that could cause it is the missing string. This solution flags all those pieces. To do this efficiently, a DFA is constructed using Aho-Corasick to build a state machine to match all available patterns. Stage two of this algorithm is to match all failures against themselves backwards. The pattern must end at that location. The number of options for the missing string is simply the length of the maximum overlap between all suffixes. All suffixes (except the empty string) are possible missing strings. Just kidding. You must also consider all suffixes of strings that failed to match. The missing string must be greater than this length. You can have a contradiction of course. In this case, the result is 0. The second stage can be accomplished efficiently using Dan Gusfield's z-values or the Knuth-Morris-Pratt NFA construction. It is also possible to use the more generalized Aho-Corasick again to solve this subproblem. Z-values is the most natural of these solutions. One last tricky thing. The strings cannot allow a duplicate. Build a trie on the reverses of the string (reuse the table construction). Build the suffix backwards and remove duplicate strings. */ import java.util.*; import java.io.*; public class flipping_font { public static void main(String[] args) throws Exception { PrintWriter out = new PrintWriter(System.out); new flipping_font(new FastScanner(System.in), out); out.close(); } String flipSequence; String rev(String s) { return new StringBuilder(s).reverse().toString(); } // Aho-corasick data. int nodeCount; int[][] nextState; int[] numTerminal; // Builds an Aho-Corasick table or just a trie depending on the parameter // passed to this function. void buildAhoCorasick(boolean isTrie, String[] patterns) { int maxNodes = 1; for (String s : patterns) maxNodes += s.length(); // Build a trie off the patterns, the trie denotes the // size of the DFA. int[][] children = new int[2][maxNodes]; int[] leafCount = new int[maxNodes]; nodeCount = 1; for (String s : patterns) { int node = 0; for (int i=0; i q = new ArrayDeque<>(); q.add(0); q.add(0); while (q.size() > 0) { int node = q.poll(); int suffixLink = q.poll(); numTerminal[node] = leafCount[node] + numTerminal[suffixLink]; for (int ch=0; ch<2; ch++) { if (children[ch][node] != 0) { nextState[ch][node] = children[ch][node]; q.add(children[ch][node]); q.add(node == 0 ? 0 : nextState[ch][suffixLink]); } else { nextState[ch][node] = nextState[ch][suffixLink]; } } } } // Dan Gusfield's z-values. int[] zvals(String s) { int L = 0, R = 0; int n = s.length(); int[] z = new int[n]; for (int i=1; i R) { L = R = i; while (R < n && s.charAt(R-L) == s.charAt(R)) R++; z[i] = R-L; R--; } else { int k = i-L; if (z[k] < R-i+1) z[i] = z[k]; else { L = i; while (R < n && s.charAt(R-L) == s.charAt(R)) R++; z[i] = R-L; R--; } } } return z; } public flipping_font(FastScanner in, PrintWriter out) { int numPatterns = in.nextInt()-1; String[] patterns = new String[numPatterns]; for (int i=0; i=initialPos-maxLen; x--) { // Add the character at position x int type = flipSequence.charAt(x) == 'H' ? 1 : 0; if (dfaState != -1) dfaState = nextState[type][dfaState]; int len = initialPos-x; if (minLen <= len) { // Only add this string to the solution set if it doesn't // match any of the strings in the pattern set. if (dfaState == -1 || numTerminal[dfaState] == 0) res++; } } out.println(res); } } class FastScanner{ private InputStream stream; private byte[] buf = new byte[1024]; private int curChar; private int numChars; public FastScanner(InputStream stream) { this.stream = stream; } int read() { if (numChars == -1) throw new InputMismatchException(); if (curChar >= numChars){ curChar = 0; try{ numChars = stream.read(buf); } catch (IOException e) { throw new InputMismatchException(); } if (numChars <= 0) return -1; } return buf[curChar++]; } boolean isSpaceChar(int c) { return c==' '||c=='\n'||c=='\r'||c=='\t'||c==-1; } boolean isEndline(int c) { return c=='\n'||c=='\r'||c==-1; } int nextInt() { return Integer.parseInt(next()); } long nextLong() { return Long.parseLong(next()); } double nextDouble() { return Double.parseDouble(next()); } String next(){ int c = read(); while (isSpaceChar(c)) c = read(); StringBuilder res = new StringBuilder(); do{ res.appendCodePoint(c); c = read(); }while(!isSpaceChar(c)); return res.toString(); } String nextLine(){ int c = read(); while (isEndline(c)) c = read(); StringBuilder res = new StringBuilder(); do{ res.appendCodePoint(c); c = read(); }while(!isEndline(c)); return res.toString(); } }