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

public class dictionary_mb_wa_1 {
    static final int INF = 1000000000;

    static String[] removeSubstrings(String[] words) {
        int n = words.length;
        boolean[] use = new boolean[n];
        Arrays.fill(use, true);
        for (int li = 0; li < n; ++li) {
            for (int ri = 0; ri < n; ++ri) {
                use[ri] &= words[li].equals(words[ri]) && ri <= li || !words[li].contains(words[ri]);
            }
        }
        int count = 0;
        for (boolean b : use) {
            if (b) {
                ++count;
            }
        }
        String[] rv = new String[count];
        for (int i = 0, j = 0; i < n; ++i) {
            if (use[i]) {
                rv[j++] = words[i];
            }
        }
        return rv;
    }

    static class Overlap {
        final int secondOffset;
        final int overlapLength;
        public Overlap(int secondOffset, int overlapLength) {
            this.secondOffset = secondOffset;
            this.overlapLength = overlapLength;
        }
        public String toString() {
            return "Overlap(" + secondOffset + "," + overlapLength + ")";
        }
        static Overlap compute(String ls, String rs) {
            int maxOverlap = 0;
            int maxPrefLen = 0;
            for (int prefLen = ls.length(); prefLen > 0; --prefLen) {
                for (int len = Math.min(prefLen, rs.length()); len > maxOverlap; --len) {
                    boolean ok = true;
                    for (int t = 0; t < len; ++t) {
                        ok &= ls.charAt(prefLen - len + t) == rs.charAt(t);
                    }
                    if (ok) {
                        maxOverlap = len;
                        maxPrefLen = prefLen;
                        break;
                    }
                }
            }
            return new Overlap(maxPrefLen - maxOverlap, maxOverlap);
        }
    }

    static int[] directedMST(int[][] graph, int root) {
        int n = graph.length;
        int[] bestParent = new int[n];
        Arrays.fill(bestParent, -1);
        for (int i = 0; i < n; ++i) {
            if (i != root) {
                for (int j = 0; j < n; ++j) {
                    if (i != j && (bestParent[i] == -1 || graph[j][i] < graph[bestParent[i]][i])) {
                        bestParent[i] = j;
                    }
                }
            }
        }
        for (int i = 0; i < n; ++i) {
            int curr = i;
            int steps = 0;
            do {
                curr = bestParent[curr];
                ++steps;
            } while (curr != -1 && curr != i && steps <= n);
            if (curr == i) {
                //We have a loop.
                boolean[] isInLoop = new boolean[n];
                do {
                    isInLoop[curr] = true;
                    curr = bestParent[curr];
                } while (curr != i);

                int[] newIndex = new int[n];
                int newVertex = 0;
                for (int ii = 0; ii < n; ++ii) {
                    if (!isInLoop[ii]) {
                        newIndex[ii] = newVertex++;
                    } else {
                        newIndex[ii] = -1;
                    }
                }

                int[] oldIncomingEdgeVertices = new int[newVertex];
                int[] oldOutgoingEdgeVertices = new int[newVertex];

                int[] backIndex = new int[newVertex];
                for (int ii = 0; ii < n; ++ii) {
                    if (!isInLoop[ii]) {
                        backIndex[newIndex[ii]] = ii;
                    }
                }

                int[][] newGraph = new int[newVertex + 1][newVertex + 1];

                for (int[] g : newGraph) {
                    Arrays.fill(g, INF);
                }

                for (int li = 0; li < n; ++li) {
                    for (int ri = 0; ri < n; ++ri) {
                        if (isInLoop[li]) {
                            if (!isInLoop[ri]) {
                                if (newGraph[newVertex][newIndex[ri]] > graph[li][ri]) {
                                    newGraph[newVertex][newIndex[ri]] = graph[li][ri];
                                    oldOutgoingEdgeVertices[newIndex[ri]] = li;
                                } else {
                                    newGraph[newVertex][newVertex] = INF;
                                }
                            }
                        } else {
                            if (!isInLoop[ri]) {
                                newGraph[newIndex[li]][newIndex[ri]] = graph[li][ri];
                            } else {
                                if (newGraph[newIndex[li]][newVertex] > graph[li][ri] - graph[bestParent[ri]][ri]) {
                                    newGraph[newIndex[li]][newVertex] = graph[li][ri] - graph[bestParent[ri]][ri];
                                    oldIncomingEdgeVertices[newIndex[li]] = ri;
                                }
                            }
                        }
                    }
                }

                int[] newBestParent = directedMST(newGraph, newIndex[root]);

                for (int ii = 0; ii < n; ++ii) {
                    if (ii != root) {    
                        if (isInLoop[ii]) {
                            int bp = newBestParent[newVertex];
                            if (oldIncomingEdgeVertices[bp] == ii) {
                                bestParent[ii] = backIndex[bp];
                            } else {
                                //bestParent was already set.
                            }
                        } else {
                            if (newBestParent[newIndex[ii]] == newVertex) {
                                bestParent[ii] = backIndex[oldOutgoingEdgeVertices[newIndex[ii]]];
                            } else {
                                bestParent[ii] = backIndex[newBestParent[newIndex[ii]]];
                            }
                        }
                    }
                }

                return bestParent;
            }
        }
        //We have a tree.
        return bestParent;
    }

    static class AnswerNode {
        static int idSource = 0;
        static List<AnswerNode> allNodes = new ArrayList<>();

        final char ch;
        final AnswerNode parent;
        final int id = ++idSource;


        public AnswerNode() {
            this.ch = ' ';
            this.parent = null;
            allNodes.add(this);
        }

        public AnswerNode(AnswerNode parent, char ch) {
            this.ch = ch;
            this.parent = parent;
            allNodes.add(this);
        }
    }

    static void restoreAnswer(String[] words, Overlap[][] overlaps, int[] parents, int current, AnswerNode myLastNode) {
        for (int ch = 0; ch < words.length; ++ch) {
            if (parents[ch] == current) {
                Overlap ovl = current == words.length ? null : overlaps[current][ch];
                AnswerNode curr = myLastNode;
                int howMuchBack = ovl == null ? 0 : words[current].length() - ovl.secondOffset - ovl.overlapLength;
                for (int i = 0; i < howMuchBack; ++i) {
                    curr = curr.parent;
                }
                for (int i = ovl == null ? 0 : ovl.overlapLength; i < words[ch].length(); ++i) {
                    curr = new AnswerNode(curr, words[ch].charAt(i));
                }
                restoreAnswer(words, overlaps, parents, ch, curr);
            }
        }
    }

    public static void solve(String[] words, PrintWriter out) {
        words = removeSubstrings(words);

        int n = words.length;
        Overlap[][] overlaps = new Overlap[n][n];
        int[][] graph = new int[n + 1][n + 1];
        for (int li = 0; li < n; ++li) {
            for (int ri = 0; ri < n; ++ri) {
                overlaps[li][ri] = li == ri ? new Overlap(0, 0) : Overlap.compute(words[li], words[ri]);
                graph[li][ri] = -overlaps[li][ri].overlapLength;
                if (graph[li][ri] == 0) {
                    graph[li][ri] = INF;
                }
            }
            graph[li][n] = INF;
        }
        int[] tree = directedMST(graph, n);
        AnswerNode root = new AnswerNode();
        restoreAnswer(words, overlaps, tree, n, root);

        out.println(AnswerNode.allNodes.size());
        for (AnswerNode nd : AnswerNode.allNodes) {
            out.println(nd.parent == null ? "0" : nd.parent.id + " " + nd.ch);
        }
    }

    public static void main(String[] args) throws IOException {
        try (BufferedReader in = new BufferedReader(new FileReader("dictionary.in"));
             PrintWriter out = new PrintWriter("dictionary.out")) {
            int n = Integer.parseInt(in.readLine());
            String[] words = new String[n];
            for (int i = 0; i < n; ++i) {
                words[i] = in.readLine();
            }
            solve(words, out);
        }
    }
}
