import static java.lang.Math.min;
import static java.util.Arrays.fill;

import java.util.*;

import ru.ifmo.testlib.*;
import ru.ifmo.testlib.Outcome.Type;

public class Check implements Checker {

	static final String NO = "No solution";
	static final int ALPHABET = 26;

	@Override
	public Outcome test(InStream inf, InStream ouf, InStream ans) {
		String firstTokenJury = ans.nextLine();
		String firstTokenCont = ouf.nextLine();
		if (firstTokenJury.equals(NO) && firstTokenCont.equals(NO)) {
			return new Outcome(Type.OK, "No solution");
		}
		if (firstTokenCont.equals(NO)) {
			return new Outcome(Type.WA, "no solution was found, but it exists");
		}
		int countWordsContestant;
		try {
			countWordsContestant = Integer.parseInt(firstTokenCont);
		} catch (NumberFormatException e) {
			return new Outcome(
					Type.PE,
					"Expected \"No solution\" or a number on the first line of output, but found : "
							+ firstTokenCont);
		}
		int countWordsJury = Integer.MAX_VALUE;
		if (!firstTokenJury.equals(NO)) {
			countWordsJury = Integer.parseInt(firstTokenJury);
		}
		if (countWordsContestant > countWordsJury) {
			return new Outcome(Type.WA,
					"Contestant found not an optimal solution");
		}
		String text = ouf.nextToken();
		check(text, inf, countWordsContestant);
		if (countWordsContestant < countWordsJury) {
			return new Outcome(Type.FAIL,
					"Contestant's answer is correct and more optimal than jury has");
		}
		return new Outcome(Type.OK, "Contestant found correct answer");
	}

	static void check(String text, InStream inf, int answer) {
		String t = inf.nextToken();
		if (!contains(text, t)) {
			throw new Outcome(Type.WA,
					"Contestant's answer doesn't contain t as a substring");
		}
		int n = inf.nextInt();
		String[] a = new String[n];
		for (int i = 0; i < n; i++) {
			a[i] = inf.nextToken();
		}
		int got = canBuildFast(text, a);
		if (got == Integer.MAX_VALUE) {
			throw new Outcome(Type.WA,
					"Contestant's answer is not a concatenation of words from the set");
		}
		if (got < answer) {
			throw new Outcome(Type.WA,
					"Contestant can get better on this string");
		}
		if (got > answer) {
			throw new Outcome(Type.WA, "Contestant's answer " + answer + " is not correct, he can get only " + got);
		}
	}

	/**
	 * Founds whether text can be represented as a concatenation of strings from
	 * set A. Time complexity is Theta(|T| * Sum(|A_i|)).It can be reduced to
	 * O(K + |T| + Sum(|A_i|)), where K is number of entries of strings from set
	 * to the text. Of course, K = O(|T| * |A|).
	 * 
	 * @param text
	 *            The text
	 * @param a
	 *            The set
	 * @return
	 */
	static boolean canBuildSlow(String text, String[] a) {
		boolean[] dp = new boolean[text.length() + 1];
		dp[0] = true;
		for (int i = 1; i < text.length(); i++) {
			all: for (String e : a) {
				if (i < e.length() || !dp[i - e.length()]) {
					continue;
				}
				for (int j = 0; j < e.length(); j++) {
					int pos = i - e.length() + j + 1;
					if (e.charAt(j) != text.charAt(pos)) {
						continue all;
					}
				}
				dp[i] = true;
				break;
			}
		}
		return dp[text.length()];
	}

	/**
	 * Founds minimal number of words from A that text can be represented as a
	 * concatenation of this words. Time complexity is O(K + |T| + Sum(|A_i|)),
	 * where K is number of entries of strings from set to the text. Of course,
	 * K = O(|T| * |A|).
	 * 
	 * @param text
	 * @param a
	 * @return
	 */
	static int canBuildFast(String text, String[] a) {
		int[] dp = new int[text.length() + 1];
		fill(dp, Integer.MAX_VALUE);
		dp[0] = 0;
		AhoCorasick ac = new AhoCorasick(a);
		List<String> temp = new ArrayList<String>();
		int pos = ac.root;
		for (int i = 1; i <= text.length(); i++) {
			pos = ac.go(pos, text.charAt(i - 1));
			ac.addAllEntries(pos, temp);
			for (String e : temp) {
				if (dp[i - e.length()] != Integer.MAX_VALUE) {
					dp[i] = min(dp[i], dp[i - e.length()] + 1);
				}
			}
			temp.clear();
		}
		return dp[text.length()];
	}

	static class AhoCorasick {
		int[][] link;
		int[] sufLink;
		String[] terminates;
		int free;
		final int root = 0;

		public AhoCorasick(String[] a) {
			int sumLen = 1;
			for (String e : a) {
				sumLen += e.length();
			}
			link = new int[ALPHABET][sumLen];
			sufLink = new int[sumLen];
			terminates = new String[sumLen];
			free = 1;
			for (int[] d : link) {
				fill(d, -1);
			}
			fill(sufLink, -1);
			for (String e : a) {
				addWord(e);
			}
			makeSufLinks();
		}

		void addAllEntries(int pos, List<String> temp) {
			if (terminates[pos] == null) {
				pos = sufLink[pos];
			}
			for (; pos > 0; pos = sufLink[pos]) {
				temp.add(terminates[pos]);
			}
		}

		int go(int pos, char c) {
			return link[c - 'a'][pos];
		}

		void addWord(String word) {
			int pos = 0;
			for (int i = 0; i < word.length(); i++) {
				int c = word.charAt(i) - 'a';
				if (link[c][pos] < 0) {
					link[c][pos] = free++;
				}
				pos = link[c][pos];
			}
			terminates[pos] = word;
		}

		void makeSufLinks() {
			int[] q = new int[sufLink.length];
			int head = 0;
			int tail = 1;
			sufLink[0] = 0;
			while (head < tail) {
				int v = q[head++];
				for (int i = 0; i < link.length; i++) {
					int u = link[i][v];
					int w = v == root ? root : link[i][sufLink[v]];
					if (u < 0) {
						link[i][v] = w;
					} else {
						q[tail++] = u;
						sufLink[u] = w;
					}
				}
			}
			for (int i = 0; i < head; i++) {
				int j = q[i];
				if (terminates[sufLink[j]] == null) {
					sufLink[j] = sufLink[sufLink[j]];
				}
			}
		}
	}

	static boolean contains(String text, String word) {
		text = word + "~" + text;
		int[] p = new int[text.length()];
		int k = -1;
		p[0] = -1;
		for (int i = 1; i < p.length; i++) {
			while (k > -1 && text.charAt(k + 1) != text.charAt(i)) {
				k = p[k];
			}
			if (text.charAt(k + 1) == text.charAt(i)) {
				k++;
			}
			if (k == word.length() - 1) {
				return true;
			}
			p[i] = k;
		}
		return false;
	}
}
