Problem:
ジグソーパズル

子供から大人まで脳トレ系パズルゲームが流行しています。我々も遅れを取らぬよう、パズルゲームを作ってみんなで遊ぶことにしました。

我々が考えたのはジグソーパズルのゲームで、未完成の部分を埋めるのに必要なピースを選ぶというものです。図 1 (a) はパズルの枠の例です。黒が埋まっている部分、白が未完成の部分です。このパズルを完成するのに使えるピースが図 1 (b) のように与えられます。この中から、枠の白い部分を埋めることができる黒いピースを 1 つ以上選択します。例えば、図 2 の選択例 1 のような組み合わせなら正解となります。一方、選択例 2 のような組み合わせではパズルは完成しないので不正解となります。また、選択したピースが余ってしまう場合も不正解です。このように、プレイヤーは適切なピースを選択することでゲームをクリアします。

そこでこのパズルゲームで用いる判定プログラムを開発することになりました。未完成のパズル、ピースの候補、そしてプレイヤーが選んだピースの組み合わせを入力とし、プレイヤーが適切なピースを選ぶことができていれば YES を、そうでなければ NO と出力するプログラムを作成してください。

この問題では、パズルの枠は H × W の配列で表し、未完成の部分を . (半角ピリオド)、完成している部分を # (半角シャープ)によって与えます。パズルの枠の大きさは最大 20 × 20 とします。また、各ピースは h × w の配列で表し、ピースを構成する部分を # 、そうでない部分を . によって与えます。与えられる各ピースは、元の状態から 90 度、180 度、270 度だけ回転することができます。また、各ピースの大きさは最大 20 × 20 とし、与えられるピースの数 n は最大 10 とします。

Input

複数のデータセットが与えられます。各データセットは以下の形式で与えられます。

H W
g1,1g1,2...g1,W
g2,1g2,2...g2,W
:
gH,1gH,2...gH,W
n
h1 w1
c11,1c11,2...c11,w1
c12,1c12,2...c12,w1
:
c1h1,1c1h1,2...c1h1,w1
:
:
hn wn
cn1,1cn1,2...cn1,wn
cn2,1cn2,2...cn2,wn
:
cnhn,1cnhn,2...cnhn,wn
p
k1 t1 t2 ... tk1
k2 t1 t2 ... tk2
:
kp t1 t2 ... tkp

1 行目に、パズルの枠の大きさ H(縦) と W(横) が与えられます。
2 行目に、文字 gi,j (. または #) からなりパズルの盤面を表す 1 行 W 文字の文字列が H 行与えられます。

続いてピースの数 n、n 個のピースの情報が与えられます。各ピースの情報として、l 番目のピースの配列のサイズ hl (縦) と wl (横)、l 番目のピースの配列が与えられます。l 番目のピースの配列として文字 cli,j (. または #) からなる 1 行 wl 文字の文字列が hl 行与えられます。

続いてプレイヤーの人数 p、i 番目のプレイヤーの選択したピースの数 ki と選択したピースの番号 tj が与えられます。

入力は２つの 0 を含む行で終了します。データセットの数は 10 を超えません。

Output

各データセットごとに、i 番目のプレイヤーが選択したピースの正否 YES または NO を i 行目に出力してください。

Sample Input

14 20
####################
###.............####
####..........######
#######.....########
########.....#######
###..###......######
###..####......#####
#########......#####
###########.....####
############.....###
###.########......##
###.#######...###.##
##...###....#####.##
####################
10
12 15
#############..
.##########....
....#####......
.....#####.....
.....######....
......######...
......######...
........#####..
.........#####.
.........######
........###...#
.....####.....#
3 3
#..
###
#..
2 2
##
##
4 10
....#####.
....######
...###...#
####.....#
6 5
....#
..###
#####
##.##
#..##
#..#.
6 4
...#
.###
####
##.#
##.#
#...
2 6
######
.#####
2 7
..#####
#######
2 13
#############
.##########..
6 9
#####....
.######..
.######..
..######.
...#####.
....#####
8
3 1 2 3
4 1 2 3 4
7 2 3 4 5 6 7 8
5 2 3 10 7 8
6 2 3 9 5 6 4
8 2 3 4 6 9 5 4 10
4 4 5 6 9
5 10 2 3 4 9
0 0

Output for the Sample Input

YES
NO
YES
NO
YES
NO
NO
YES

Solution in JAVA (judge verdict: Time Limit Exceeded)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class Main {
	private	static	BufferedReader	br = null;

	static {
		br = new BufferedReader(new InputStreamReader(System.in));
	}
 
    /**
     * @param args
     */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Map	bd = null;

		while ((bd = parseBoard()) != null) {
			int	bc = 0;

			if ((bc = parseCnt()) > 0) {
				Map[]	pc = new Map[bc];
				int		hc = 0;
				for (int i = 0; i < bc; i++) {
					pc[i] = parsePiece();
				}

				if ((hc = parseCnt()) > 0) {
					while (hc-- > 0) {
						int[]	ptn = parsePattern(pc);
						boolean res = (ptn[ptn.length-1] == bd.sz);
						res = (res) ? (solve(bd, pc, ptn, 0, 0) == 0) : res;
						System.out.println((res) ? "YES" : "NO");
					}
				}
			}
		}
	}
 
	private static int solve(Map bd, Map[] pc, int[] ptn, int upc, int filled) {
		if (bd.sz == filled) {
			// 全部の空き地が埋まったら正解
			return 0;
		}

		for(int i = 0; i < bd.ln[0][0]; i++) {
			for(int j = 0; j < bd.ln[0][1]; j++) {
				if ((bd.mp[0][i] & (1<<(30-j))) != 0) {
					boolean	isMatchAny = false;

					for (int n = 0; n < ptn.length-1; n++) {
						int	k = ptn[n];

						// 一度使ったピースは除外
						if ((upc & 1<<n) != 0) continue;

						// ピースの面積が空き地の面積を超える場合は除外
						if (bd.sz < filled+pc[k].sz) continue;

						for (int l = 0; l < pc[k].mp.length; l++) {
							// これ以上ピースはない場合は除外
							if (pc[k].mp[l] == null) break;

							int	xs = pc[k].st[l][1];
							int	xe = bd.ln[0][1]-pc[k].ln[l%pc[k].ln.length][1]+pc[k].st[l][1];
							int	ys = pc[k].st[l][0];
							int	ye = bd.ln[0][0]-pc[k].ln[l%pc[k].ln.length][0]+pc[k].st[l][0];
							int	dj = j-pc[k].st[l][1];
							int	di = i-pc[k].st[l][0];

							// ピースが置ける範囲内にある場合はマッチング
							if (xs <= j && j <= xe && ys <= i && i <= ye) {
								boolean	isMatch = true;

								for (int m = 0; m < pc[k].ln[l%pc[k].ln.length][0]; m++) {
									if ((bd.mp[0][di+m] & (pc[k].mp[l][m]>>dj)) != (pc[k].mp[l][m]>>dj)) {
										// ピースが合わない場合はマッチングを打ち切り
										isMatch = false;
										break;
									}
								}

								if (isMatch) {
									int	nSt = 0;

									isMatchAny = true;
									putPiece(j, i, l, bd, pc[k], false);
									nSt = solve(bd, pc, ptn, upc | 1<<n, filled+pc[k].sz);
									putPiece(j, i, l, bd, pc[k], true);

									if (nSt == 0 || nSt == -2) return nSt;
								}
							}
						}
					}

					if (!isMatchAny) {
						// どのピースも嵌らなかったら前に戻る
						return -1;
					}
				}
			}
		}

		return -1;
	}

	private static void putPiece(int x, int y, int l, Map bd, Map pc, boolean rm) {
		int	dx = x-pc.st[l][1];
		int	dy = y-pc.st[l][0];
		for (int i = 0; i < pc.ln[l%pc.ln.length][0]; i++) {
			if (rm) {
				bd.mp[0][dy+i] |= pc.mp[l][i]>>dx;
			} else {
				bd.mp[0][dy+i] &= ~(pc.mp[l][i]>>dx);
			}
		}
	}

	private static int[] parsePattern(Map[] pc) {
		int[]	ptn   = null;
        String	stdin = null;

        if ((stdin = parseStdin()) != null) {
        	String[]	lines = stdin.split(" ");
        	int			max   = Integer.parseInt(lines[0]);

        	ptn = new int[lines.length];
        	for (int i = 0; i < max; i++) {
        		ptn[i]    = Integer.parseInt(lines[i+1])-1;
        		ptn[max] += pc[ptn[i]].sz;
        	}
        }
 
        return ptn;
	}

	private static int[] parseSize() {
		int[]	sz    = null;
        String	stdin = null;

        if ((stdin = parseStdin()) != null) {
        	if (!stdin.equals("0 0")) {
            	String[]	lines = stdin.split(" ");
            	sz    = new int[2];
            	sz[0] = Integer.parseInt(lines[0]);
            	sz[1] = Integer.parseInt(lines[1]);
        	}
        }
 
        return sz;
	}

	private static Map parseBoard() {
		Map		bd = null;
		int[]	sz = parseSize();

		if (sz != null) {
			bd = new Map();
			bd.mp = new int[1][sz[0]];
			bd.ln = new int[1][];
			bd.ln[0] = sz;

			for (int i = 0; i < sz[0]; i++) {
				String	line  = null;
		        if ((line = parseStdin()) != null) {
		        	for (int j = 0; j < sz[1]; j++) {
		        		if (line.charAt(j) == '.') {
			        		bd.mp[0][i] |= (1<<(30-j));
			        		bd.sz++;
		        		}
		        	}
		        }
			}
		}

		return bd;
	}

	private static Map parsePiece() {
		Map		bd = null;
		int[]	ln = parseSize();
		int[]	wk = null;
		int[]	st = null;
		boolean	fn = false;

		if (ln != null) {
			bd = new Map();
			bd.mp = new int[4][];
			bd.st = new int[4][];
			if (ln[0] != ln[1]) {
				bd.ln = new int[2][2];
				bd.ln[0][0] = bd.ln[1][1] = ln[0];
				bd.ln[0][1] = bd.ln[1][0] = ln[1];
			} else {
				bd.ln = new int[1][2];
				bd.ln[0][0] = ln[0];
				bd.ln[0][1] = ln[1];
			}

			// 0度
			if (!fn) {
				wk = new int[bd.ln[0][0]];
				st = null;

				for (int i = 0; i < wk.length; i++) {
					String	line  = null;
			        if ((line = parseStdin()) != null) {
			        	for (int j = 0, k = line.indexOf("#", j); k != -1; k = line.indexOf("#", k+1)) {
			        		if (st == null) {
			        			st = new int[2];
			        			st[0] = i;
			        			st[1] = k;
			        		}
			        		wk[i] |= (1<<(30-k));
			        		bd.sz++;
			        	}
			        }
				}

				bd.mp[0] = wk;
				bd.st[0] = st;
			}

			// 90度
			if (!fn) {
				wk = new int[bd.ln[0][1%bd.ln.length]];
				st = null;
				fn = (ln[0] != ln[1]) ? false : true;

				for (int i = 0; i < wk.length; i++) {
		        	for (int j = 0; j < bd.ln[0][0]; j++) {
		        		if ((bd.mp[0][bd.ln[0][0]-j-1] & (1<<(30-i))) != 0) {
			        		if (st == null) {
			        			st = new int[2];
			        			st[0] = i;
			        			st[1] = j;
			        		}
			        		wk[i] |= (1<<(30-j));
		        		}
		        	}
		        	fn = (fn) ? bd.mp[0][i] == wk[i] : false;
				}

				if (!fn) {
					bd.mp[1] = wk;
					bd.st[1] = st;
				}
			}

			// 180度
			if (!fn) {
				wk = new int[bd.mp[0].length];
				st = null;
				fn = true;

				for (int i = 0; i < wk.length; i++) {
		        	for (int j = 0; j < bd.ln[0][1]; j++) {
		        		if ((bd.mp[0][wk.length-1-i] & (1<<(30-(bd.ln[0][1]-1-j)))) != 0) {
			        		if (st == null) {
			        			st = new int[2];
			        			st[0] = i;
			        			st[1] = j;
			        		}
			        		wk[i] |= (1<<(30-j));
		        		}
		        	}
		        	fn = (fn) ? bd.mp[0][i] == wk[i] : false;
				}

				if (!fn) {
					bd.mp[2] = wk;
					bd.st[2] = st;
				}
			}

			// 270度
			if (!fn) {
				wk = new int[bd.mp[1].length];
				st = null;
				fn = true;

				for (int i = 0; i < wk.length; i++) {
		        	for (int j = 0; j < bd.ln[0][0]; j++) {
		        		if ((bd.mp[1][wk.length-1-i] & (1<<(30-(bd.ln[0][0]-1-j)))) != 0) {
			        		if (st == null) {
			        			st = new int[2];
			        			st[0] = i;
			        			st[1] = j;
			        		}
			        		wk[i] |= (1<<(30-j));
		        		}
		        	}
		        	fn = (fn) ? bd.mp[1][i] == wk[i] : false;
				}

				if (!fn) {
					bd.mp[3] = wk;
					bd.st[3] = st;
				}
			}
		}

		return bd;
	}

	private static int parseCnt() {
		int     cnt = 0;
		String  str = null;

		if ((str = parseStdin()) != null) {
			cnt = Integer.parseInt(str);
		}

		return cnt;
	}
 
	private static String parseStdin() {
        String  stdin = null;
        
        try {
        	String  tmp = br.readLine();
        	if (!tmp.isEmpty()) stdin = tmp;
        }
        catch (IOException e) {}
 
        return stdin;
	}
}

class Map {
	int[][]	mp = null;
	int[][]	ln = null;
	int[][]	st = null;
	int		sz = 0;
}