import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Set;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.FileInputStream;
import java.util.StringTokenizer;
import java.util.HashSet;
import java.io.InputStream;
 
/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class ascii_petr {
    public static void main(String[] args) {
        InputStream inputStream;
        try {
            inputStream = new FileInputStream("ascii.in");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        OutputStream outputStream;
        try {
            outputStream = new FileOutputStream("ascii.out");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        InputReader_petr in = new InputReader_petr(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        ASCII_petr_0 solver = new ASCII_petr_0();
        solver.solve(1, in, out);
        out.close();
    }
}
 
class ASCII_petr_0 {
    public void solve(int testNumber, InputReader_petr in, PrintWriter out) {
        int k = in.nextInt();
        int w = in.nextInt();
        int h = in.nextInt();
        boolean[][][] pieces = new boolean[k][3 * h][3 * w];
        for (int i = 0; i < k; ++i) {
            for (int row = 1; row < 3 * h - 1; ++row) {
                String s = in.next();
                for (int col = 1; col < 3 * w - 1; ++col) {
                    char ch = s.charAt(col - 1);
                    if (ch == (char) ('A' + i))
                        pieces[i][row][col] = true;
                    else if (ch != '.') {
                        out.println("WHATTHEHELL");
                        return;
                    }
                }
            }
        }
        int ntop = 0;
        for (int i = 0; i < k; ++i) {
            boolean top = true;
            for (int col = w; col < 2 * w; ++col)
                if (pieces[i][h - 1][col] || !pieces[i][h][col])
                    top = false;
            if (top) ++ntop;
        }
        int nw = ntop;
        if (k % nw != 0)  {
            out.println("WHATTHEHELL");
            return;
        }
        int nh = k / nw;
        char[][] res = new char[h * nh][w * nw];
        if (!tryit(pieces, k, w, h, nw, nh, res)) {
            throw new RuntimeException();
        }
        out.println(w * nw + " " + h * nh);
        for (int r = 0; r < res.length; ++r) {
            for (int c = 0; c < res[r].length; ++c)
                out.print(res[r][c]);
            out.println();
        }
    }
 
    int w;
    int h;
    int nw;
    int nh;
    int k;
    char[][] res;
    boolean[][][] pieces;
    boolean[] used;
    boolean[][] same;
 
    private boolean tryit(boolean[][][] pieces, int k, int w, int h, int nw, int nh, char[][] res) {
        if (nw > nh) {
            boolean[][][] np = new boolean[k][3 * w][3 * h];
            for (int i = 0; i < k; ++i)
                for (int r = 0; r < 3 * h; ++r)
                    for (int c = 0; c < 3 * w; ++c) {
                        np[i][c][r] = pieces[i][r][c];
                    }
            char[][] nres = new char[w * nw][h * nh];
            if (!tryit(np, k, h, w, nh, nw, nres)) return false;
            for (int c = 0; c < w * nw; ++c)
                for (int r = 0; r < h * nh; ++r)
                    res[r][c] = nres[c][r];
            return true;
        }
        this.k = k;
        this.w = w;
        this.h = h;
        this.nw = nw;
        this.nh = nh;
        this.res = res;
        this.pieces = pieces;
        this.used = new boolean[k];
        same = new boolean[k][k];
        for (int i = 0; i < k; ++i)
            for (int j = 0; j < k; ++j) {
                same[i][j] = true;
                for (int r = 0; r < 3 * h; ++r)
                    for (int c = 0; c < 3 * w; ++c) {
                        if (pieces[i][r][c] != pieces[j][r][c])
                            same[i][j] = false;
                    }
            }
        return rec(0, 0);
    }
 
    Set<Long> seen = new HashSet<Long>();
 
    private boolean rec(int br, int bc) {
        if (br >= nh) {
            return true;
        }
        if (bc >= nw) {
            return rec(br + 1, 0);
        }
        long hash = 0;
        for (int i = 0; i < k; ++i) hash = hash * 3137 + (used[i] ? 1 : 0);
        for (int r = 0; r < h * nh; ++r)
            for (int c = 0; c < w * nw; ++c)
                hash = hash * 3137 + (res[r][c] == 0 ? 1 : 0);
        if (!seen.add(hash)) return false;
        outer: for (int what = 0; what < k; ++what) if (!used[what]) {
            for (int prev = 0; prev < what; ++prev)
                if (!used[prev] && same[what][prev])
                    continue outer;
            boolean[][] piece = pieces[what];
            boolean isrightmost = true;
            boolean isbottommost = true;
            boolean isleftmost = true;
            boolean istopmost = true;
            for (int c = w; c < 2 * w; ++c) {
                if (piece[2 * h][c] || !piece[2 * h - 1][c]) isbottommost = false;
                if (piece[h - 1][c] || !piece[h][c]) istopmost = false;
            }
            for (int r = h; r < 2 * h; ++r) {
                if (piece[r][2 * w] || !piece[r][2 * w - 1]) isrightmost = false;
                if (piece[r][w - 1] || !piece[r][w]) isleftmost = false;
            }
            if (isrightmost != (bc == nw - 1)) continue;
            if (isbottommost != (br == nh - 1)) continue;
            if (isleftmost != (bc == 0)) continue;
            if (istopmost != (br == 0)) continue;
            int dr = -h + br * h;
            int dc = -w + bc * w;
            for (int r = 0; r < 3 * h; ++r)
                for (int c = 0; c < 3 * w; ++c)
                    if (piece[r][c]) {
                        int nr = r + dr;
                        int nc = c + dc;
                        if (nr < 0 || nr >= h * nh || nc < 0 || nc >= w * nw || res[nr][nc] != 0)
                            continue outer;
                    }
            for (int r = 0; r < 3 * h; ++r)
                for (int c = 0; c < 3 * w; ++c)
                    if (piece[r][c]) {
                        int nr = r + dr;
                        int nc = c + dc;
                        res[nr][nc] = (char) ('A' + what);
                    }
            boolean fine = true;
            if (br > 0) {
                for (int r = -h; r < 0; ++r)
                    for (int c = 0; c < w; ++c)
                        if (res[br * h + r][bc * w + c] == 0) fine = false;
            }
            if (fine) {
                used[what] = true;
                if (rec(br, bc + 1)) return true;
                used[what] = false;
            }
            for (int r = 0; r < 3 * h; ++r)
                for (int c = 0; c < 3 * w; ++c)
                    if (piece[r][c]) {
                        int nr = r + dr;
                        int nc = c + dc;
                        res[nr][nc] = 0;
                    }
        }
        return false;
    }
}
 
class InputReader_petr {
    public BufferedReader reader;
    public StringTokenizer tokenizer;
 
    public InputReader_petr(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
        tokenizer = null;
    }
 
    public String next() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return tokenizer.nextToken();
    }
 
    public int nextInt() {
        return Integer.parseInt(next());
    }
 
    }