import java.util.List;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.ArrayList;
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.io.InputStream;
 
/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class dictionary_petr {
    public static void main(String[] args) {
        InputStream inputStream;
        try {
            inputStream = new FileInputStream("dictionary.in");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        OutputStream outputStream;
        try {
            outputStream = new FileOutputStream("dictionary.out");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        InputReader_petr in = new InputReader_petr(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        Dictionary_petr_0 solver = new Dictionary_petr_0();
        solver.solve(1, in, out);
        out.close();
    }
}
 
class Dictionary_petr_0 {
    static class Node {
        int index;
        Node parent;
        char parentLetter;
    }
 
    static class Branching {
        int root;
        int cost;
        int[] parent;
    }
 
    List<Node> allNodes = new ArrayList<Node>();
 
    static final int INF = (int) 1e9;
 
    public void solve(int testNumber, InputReader_petr in, PrintWriter out) {
        int n = in.nextInt();
        String[] w = new String[n];
        for (int i = 0; i < n; ++i) w[i] = in.next();
        int[][] canShare = new int[n][n];
        for (int[] x : canShare) Arrays.fill(x, INF);
        int[][] sip = new int[n][n];
        for (int[] x : sip) Arrays.fill(x, -1);
        int[][] sjp = new int[n][n];
        for (int[] x : sjp) Arrays.fill(x, -1);
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < n; ++j) if (i != j)
                for (int ip = 0; ip <= w[i].length(); ++ip)
                    for (int jp = 0; jp <= w[j].length(); ++jp) {
                        String s1 = w[i].substring(0, w[i].length() - ip);
                        String s2 = w[j].substring(0, w[j].length() - jp);
                        if (s1.endsWith(s2)) {
                            int sh = -s2.length();
                            if (sh < canShare[i][j]) {
                                canShare[i][j] = sh;
                                sip[i][j] = ip;
                                sjp[i][j] = jp;
                            }
                        }
                    }
        Branching branching = minBranching(canShare);
        boolean[] used = new boolean[n];
        Node root = new Node();
        allNodes.add(root);
        Node[] where = new Node[n];
        for (int step = 0; step < n; ++step) {
            int besti = -1;
            int bestj = -1;
            for (int i = 0; i < n; ++i) if (!used[i]) {
                int maxj = -1;
                for (int j = 0; j < n; ++j) if (used[j] && branching.parent[i] == j) {
                    maxj = j;
                }
                if (maxj >= 0) {
                    besti = i;
                    bestj = maxj;
                }
            }
            if (besti < 0) {
                besti = branching.root;
            }
            used[besti] = true;
            if (bestj < 0) {
                where[besti] = root;
                Node nroot = apply(where[besti], w[besti]);
                if (root.parent != null) root = nroot;
            } else {
                int jp = sip[bestj][besti];
                int ip = sjp[bestj][besti];
                Node attachment = where[bestj];
                for (int i = 0; i < jp; ++i) attachment = attachment.parent;
                if (ip > 0) {
                    Node dummy = new Node();
                    allNodes.add(dummy);
                    where[besti] = dummy;
                    dummy = apply(dummy, w[besti].substring(w[besti].length() - ip + 1));
                    dummy.parent = attachment;
                    dummy.parentLetter = w[besti].charAt(w[besti].length() - ip);
                } else {
                    where[besti] = attachment;
                }
                Node nroot = apply(attachment, w[besti].substring(0, w[besti].length() - ip));
                if (root.parent != null) throw new RuntimeException();
            }
        }
        for (int i = 0; i < allNodes.size(); ++i) allNodes.get(i).index = i + 1;
        out.println(allNodes.size());
        for (Node nn : allNodes) {
            if (nn.parent == null) out.println(0); else out.println(nn.parent.index + " " + nn.parentLetter);
        }
 
    }
 
    private Branching minBranching(int[][] g) {
        Branching res = null;
        for (int root = 0; root < g.length; ++root) {
            Branching br = minRooted(g, root);
            if (res == null || br.cost < res.cost) {
                res = br;
            }
        }
        return res;
    }
 
    private Branching minRooted(int[][] g, int root) {
        int n = g.length;
        int[][] ng = new int[n][n];
        int delta = 0;
        int[] parent = new int[n];
        parent[root] = -1;
        for (int j = 0; j < n; ++j) if (j != root) {
            int min = INF;
            for (int i = 0; i < n; ++i) if (g[i][j] < min) {
                min = g[i][j];
                parent[j] = i;
            }
            for (int i = 0; i < n; ++i) ng[i][j] = g[i][j] - min;
            delta += min;
        }
        int[] cycle = findCycle(parent);
        if (cycle == null) {
            Branching res = new Branching();
            res.cost = delta;
            res.parent = parent;
            res.root = root;
            return res;
        }
        boolean[] incycle = new boolean[n];
        for (int x : cycle) incycle[x] = true;
        int[] mapped = new int[n];
        int mcnt = 1;
        for (int i = 0; i < n; ++i) {
            if (!incycle[i]) {
                mapped[i] = mcnt++;
            } else {
                mapped[i] = 0;
            }
        }
        int[][] mg = new int[mcnt][mcnt];
        int[][] mgvia = new int[mcnt][mcnt];
        for (int[] x : mg) Arrays.fill(x, INF);
        for (int[] x : mgvia) Arrays.fill(x, -1);
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < n; ++j) {
                int mi = mapped[i];
                int mj = mapped[j];
                if (mi == mj) continue;
                if (ng[i][j] < mg[mi][mj]) {
                    mg[mi][mj] = ng[i][j];
                    if (mi == 0) mgvia[mi][mj] = i; else if (mj == 0) mgvia[mi][mj] = j;
                }
            }
        Branching small = minRooted(mg, mapped[root]);
        for (int i = 0; i < n; ++i) if (i != root && !incycle[i]) {
            int sp = small.parent[mapped[i]];
            if (sp == 0) {
                parent[i] = mgvia[0][mapped[i]];
            } else {
                for (int j = 0; j < n; ++j) if (mapped[j] == sp)
                    parent[i] = j;
            }
        }
        {
            int sp = small.parent[0];
            int j;
            for (j = 0; j < n; ++j) if (mapped[j] == sp) break;
            int cycleRoot = mgvia[sp][0];
            parent[cycleRoot] = j;
        }
        Branching res = new Branching();
        res.cost = small.cost + delta;
        res.root = root;
        res.parent = parent;
        return res;
    }
 
    private int[] findCycle(int[] parent) {
        int n = parent.length;
        int[] mark = new int[n];
        Arrays.fill(mark, 0);
        int stage = 0;
        for (int i = 0; i < n; ++i) if (mark[i] == 0) {
            ++stage;
            int j = i;
            while (j >= 0 && mark[j] == 0) {
                mark[j] = stage;
                j = parent[j];
            }
            if (j >= 0 && mark[j] == stage) {
                int[] res = new int[n];
                int rp = 0;
                int k = j;
                do {
                    res[rp++] = k;
                    k = parent[k];
                } while (k != j);
                res = Arrays.copyOf(res, rp);
                return res;
            }
        }
        return null;
    }
 
    private Node apply(Node node, String s) {
        if (s.isEmpty()) return node;
        if (node.parent != null) {
            if (node.parentLetter != s.charAt(s.length() - 1)) throw new RuntimeException();
            return apply(node.parent, s.substring(0, s.length() - 1));
        }
        Node newNode = new Node();
        node.parent = newNode;
        node.parentLetter = s.charAt(s.length() - 1);
        allNodes.add(newNode);
        return apply(newNode, s.substring(0, s.length() - 1));
    }
}
 
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());
    }
 
    }