import java.io.*;
import java.util.*;

public class ascii_va_tl2 implements Runnable {
	public static void main(String[] args) {
		new Thread(new ascii_va_tl2()).run();
	}

	BufferedReader br;
	StringTokenizer in;
	PrintWriter out;

	public String nextToken() throws IOException {
		while (in == null || !in.hasMoreTokens()) {
			in = new StringTokenizer(br.readLine());
		}

		return in.nextToken();
	}

	public int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	public long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	public double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}

	public void write(boolean[][] a) {
		for (int i = 0; i < a.length; i++) {
			for (int j = 0; j < a[i].length; j++) {
				System.out.print(a[i][j] ? '*' : '.');
			}
			System.out.println();
		}
	}

	int[] dx = { 1, 0, -1, 0 };
	int[] dy = { 0, 1, 0, -1 };

	public class Piece {
		boolean[][] a;
		char c;
		boolean[] free = new boolean[4];

		public Piece(char[][] x) {
			a = new boolean[x.length][x[0].length];
			for (int i = 0; i < x.length; i++) {
				for (int j = 0; j < x[i].length; j++) {
					if (x[i][j] != '.') {
						a[i][j] = true;
						c = x[i][j];
					}
				}
			}
		}

		public void isFree() {
			int w = (a.length + 2) / 3;
			int h = (a[0].length + 2) / 3;

			for (int j = 0; j < a[0].length; j++) {
				for (int i = 2 * w - 1; i < a.length; i++) {
					free[0] |= a[i][j];
				}
				for (int i = 0; i < w - 1; i++) {
					free[2] |= a[i][j];
				}
			}

			for (int i = 0; i < a.length; i++) {
				for (int j = 2 * h - 1; j < a[i].length; j++) {
					free[1] |= a[i][j];
				}
				for (int j = 0; j < h - 1; j++) {
					free[3] |= a[i][j];
				}
			}

			for (int j = h - 1; j < 2 * h - 1; j++) {
				free[0] |= !a[2 * w - 2][j];
				free[2] |= !a[w - 1][j];
			}

			for (int i = w - 1; i < 2 * w - 1; i++) {
				free[1] |= !a[i][2 * h - 2];
				free[3] |= !a[i][h - 1];
			}

		}

		public boolean isFree(int k) {
			return !free[k];
		}

		public boolean can(Piece p, int dir) {
			if (this.isFree(dir) || p.isFree(dir + 2)) {
				return false;
			}
			int w = (a.length + 2) / 3;
			int h = (a[0].length + 2) / 3;
			if (dir == 0) {
				boolean[][] union = new boolean[4 * w][3 * h];
				for (int i = 0; i < a.length; i++) {
					for (int j = 0; j < a[i].length; j++) {
						if (union[i + 1][j + 1] && a[i][j])
							return false;
						union[i + 1][j + 1] |= a[i][j];
						union[i + w + 1][j + 1] = p.a[i][j];
					}
				}

				return one(union);
				// for (int i = w - 1; i < 3 * w - 1; i++) {
				// for (int j = h; j < 2 * h - 2; j++) {
				// if (!union[i][j])
				// return false;
				// }
				// }
			} else {
				boolean[][] union = new boolean[3 * w][4 * h];
				for (int i = 0; i < a.length; i++) {
					for (int j = 0; j < a[i].length; j++) {
						if (union[i + 1][j + 1] && a[i][j])
							return false;
						union[i + 1][j + 1] |= a[i][j];
						union[i + 1][j + h + 1] = p.a[i][j];
					}
				}

				return one(union);
				// for (int i = w; i < 2 * w - 2; i++) {
				// for (int j = h - 1; j < 3 * h - 1; j++) {
				// if (!union[i][j])
				// return false;
				// }
				// }
			}
			// return true;
		}
	}

	public boolean one(boolean[][] a) {
		dfs(0, 0, a);
		for (int i = 0; i < a.length; i++) {
			for (int j = 0; j < a[i].length; j++) {
				if (!a[i][j])
					return false;
			}
		}
		return true;
	}

	public void dfs(int x, int y, boolean[][] was) {
		if (was[x][y])
			return;
		was[x][y] = true;
		for (int i = 0; i < 4; i++) {
			int xx = x + dx[i];
			int yy = y + dy[i];
			if (xx < 0 || xx >= was.length || yy < 0 || yy >= was[0].length)
				continue;
			dfs(xx, yy, was);
		}
	}

	HashSet<Integer>[][] edges;
	int[][] ans;
	Piece[] pieces;

	boolean[] take;
	int W, H;

	public boolean gen(int w, int h) {
		if (w == W)
			return true;

		if (h == H) {
			return gen(w + 1, 0);
		}

		for (int i = 0; i < take.length; i++) {
			if (take[i])
				continue;

			boolean good = true;
			for (int j = 0; j < dx.length; j++) {
				if (w + dx[j] < 0 || w + dx[j] >= W || h + dy[j] < 0
						|| h + dy[j] >= H)
					good &= pieces[i].isFree(j);
			}

			if (!good)
				continue;

			if (w != 0 && !edges[ans[w - 1][h]][0].contains(i)) {
				continue;
			}
			if (h != 0 && !edges[ans[w][h - 1]][1].contains(i)) {
				continue;
			}

			take[i] = true;
			ans[w][h] = i;

			if (gen(w, h + 1))
				return true;

			take[i] = false;
		}

		return false;
	}

	public void solve() throws IOException {
		int n = nextInt();
		int h = nextInt();
		int w = nextInt();

		pieces = new Piece[n];
		for (int i = 0; i < pieces.length; i++) {
			char[][] c = new char[3 * w - 2][3 * h - 2];
			for (int j = 0; j < c.length; j++) {
				c[j] = nextToken().toCharArray();
			}
			pieces[i] = new Piece(c);
			pieces[i].isFree();
		}

		// System.err.println(pieces[1].can(pieces[2], 1));
		// if (1 == 1)
		// return;

		edges = new HashSet[n][2];

		W = 0;
		for (int i = 0; i < n; i++) {
			if (pieces[i].isFree(3)) {
				W++;
			}
			edges[i][0] = new HashSet<Integer>();
			edges[i][1] = new HashSet<Integer>();
			for (int j = 0; j < pieces.length; j++) {
				for (int k = 0; k < 2; k++) {
					if (pieces[i].can(pieces[j], k))
						edges[i][k].add(j);
				}
			}
		}

		H = n / W;
		take = new boolean[n];
		ans = new int[W][H];
		if (!gen(0, 0))
			throw new AssertionError();

		char[][] ret = new char[w * W][h * H];
		for (int i = 0; i < W; i++) {
			for (int j = 0; j < H; j++) {
				for (int dx = 0; dx < 3 * w - 2; dx++) {
					for (int dy = 0; dy < 3 * h - 2; dy++) {
						int xx = i * w + dx - (w - 1);
						int yy = j * h + dy - (h - 1);
						if (xx < 0 || xx >= w * W || yy < 0 || yy >= h * H)
							continue;
						ret[xx][yy] = pieces[ans[i][j]].a[dx][dy] ? pieces[ans[i][j]].c
								: ret[xx][yy];
					}
				}
			}
		}

		out.println(ret[0].length + " " + ret.length);
		for (int i = 0; i < ret.length; i++) {
			out.println(new String(ret[i]));
		}
	}

	public void run() {
		try {
			br = new BufferedReader(new FileReader("ascii.in"));
			out = new PrintWriter("ascii.out");

			solve();

			out.close();
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}
}
