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

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author Egor Kulikov (egor@egork.net)
 */
public class dictionary_egor {
	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_egor in = new InputReader_egor(inputStream);
		OutputWriter out = new OutputWriter(outputStream);
		Dictionary_egor_0 solver = new Dictionary_egor_0();
		solver.solve(1, in, out);
		out.close();
	}

    public static void solve(String[] strings, Writer out) {
        StringBuilder test = new StringBuilder();
        test.append(strings.length).append("\n");
        for (String s : strings) {
            test.append(s).append("\n");
        }
        byte[] data = test.toString().getBytes();
        new Dictionary_egor_0().solve(1, new InputReader_egor(new ByteArrayInputStream(data)), new OutputWriter(out));
    }
}

class Dictionary_egor_0 {
	char[] lastLetter;

	public void solve(int testNumber, InputReader_egor in, OutputWriter out) {
		int count = in.readInt();
		String[] words = IOUtils.readStringArray(in, count);
		Indexer<String> indexer = buildIndexer(words);
		Graph graph = buildGraph(indexer);
		int[] parent = buildMST(graph, new boolean[graph.vertexCount()]);
		IndependentSetSystem setSystem = new RecursiveIndependentSetSystem(parent.length);
		for (int i = 1; i < parent.length; i++) {
			if (graph.weight(parent[i]) == 0)
				setSystem.join(i, graph.source(parent[i]));
		}
		Indexer<Integer> finalVertices = new Indexer<Integer>();
		for (int i = 0; i < parent.length; i++) {
			if (setSystem.get(i) == i)
				finalVertices.get(i);
		}
		int[] finalParent = new int[finalVertices.size()];
		Arrays.fill(finalParent, -1);
		char[] label = new char[finalParent.length];
		for (int i = 1; i < parent.length; i++) {
			if (graph.weight(parent[i]) == 1) {
				parent[i] = graph.source(parent[i]);
				int current = finalVertices.get(setSystem.get(i));
				finalParent[current] = finalVertices.get(setSystem.get(parent[i]));
				label[current] = lastLetter[i];
			}
		}
		out.printLine(finalParent.length);
		for (int i = 0; i < finalParent.length; i++) {
			if (finalParent[i] == -1)
				out.printLine(0);
			else
				out.printLine(finalParent[i] + 1, label[i]);
		}
	}

	private int[] buildMST(Graph graph, boolean[] ignore) {
		int vertexCount = graph.vertexCount();
		int[] parent = new int[vertexCount];
		parent[0] = -1;
		for (int i = 1; i < vertexCount; i++) {
			if (ignore[i])
				continue;
			parent[i] = -1;
			for (int j = graph.firstInbound(i); j != -1; j = graph.nextInbound(j)) {
				if (graph.weight(j) == 0) {
					parent[i] = j;
					break;
				}
			}
			if (parent[i] == -1)
				parent[i] = graph.firstInbound(i);
		}
		int[] processed = new int[vertexCount];
		processed[0] = 2;
		for (int i = 1; i < vertexCount; i++) {
			if (processed[i] != 0 || ignore[i])
				continue;
			int current = i;
			do {
				processed[current] = 1;
				current = graph.source(parent[current]);
			} while (processed[current] == 0);
			if (processed[current] == 2) {
				current = i;
				do {
					processed[current] = 2;
					current = graph.source(parent[current]);
				} while (processed[current] == 1);
				continue;
			}
			int minEdge = 1;
			int start = current;
			do {
				if (graph.weight(parent[current]) == 0) {
					minEdge = 0;
				}
				current = graph.source(parent[current]);
				processed[current] = 3;
			} while (current != start);
			Graph newGraph = new Graph(vertexCount, graph.edgeCount());
			for (int j = 0; j < graph.edgeCount(); j++) {
				int from = graph.source(j);
				int to = graph.destination(j);
				if (processed[from] == 3 && processed[to] == 3) {
					if (from == start)
						newGraph.addSimpleEdge(to, to);
					else
						newGraph.addSimpleEdge(from, from);
				} else if (processed[from] == 3)
					newGraph.addWeightedEdge(start, to, graph.weight(j));
				else if (processed[to] == 3)
					newGraph.addWeightedEdge(from, start, graph.weight(j) - (graph.weight(parent[to]) - minEdge));
				else
					newGraph.addWeightedEdge(from, to, graph.weight(j));
			}
//			boolean[] wasIgnore = ignore.clone();
			for (int j = 0; j < vertexCount; j++) {
				if (processed[j] == 3 && j != start)
					ignore[j] = true;
			}
			int[] newParent = buildMST(newGraph, ignore.clone());
//			int[] realParent = new int[parent.length];
//			realParent[0] = -1;
//			for (int j = 1; j < parent.length; j++) {
//				if (wasIgnore[j])
//					realParent[j] = -2;
//				else
//					realParent[j] = graph.source(parent[j]);
//			}
//			System.err.println(Arrays.toString(ArrayUtils.createOrder(parent.length)));
//			System.err.println(Arrays.toString(realParent));
			for (int j = 1; j < newParent.length; j++) {
				if (!ignore[j])
					parent[graph.destination(newParent[j])] = newParent[j];
			}
//			for (int j = 1; j < parent.length; j++) {
//				if (wasIgnore[j])
//					realParent[j] = -2;
//				else
//					realParent[j] = graph.source(parent[j]);
//			}
//			System.err.println(Arrays.toString(realParent));
			break;
		}
		return parent;
	}

	private Graph buildGraph(Indexer<String> indexer) {
		Graph graph = new Graph(indexer.size());
		lastLetter = new char[indexer.size()];
		for (Map.Entry<String, Integer> entry : indexer.entrySet()) {
			String word = entry.getKey();
			int id = entry.getValue();
			for (int i = 1; i < word.length(); i++) {
				String part = word.substring(i);
				if (indexer.containsKey(part))
					graph.addWeightedEdge(id, indexer.get(part), 0);
			}
			if (word.length() != 0) {
				graph.addWeightedEdge(indexer.get(word.substring(0, word.length() - 1)), id, 1);
				lastLetter[id] = word.charAt(word.length() - 1);
			}
		}
		return graph;
	}

	private Indexer<String> buildIndexer(String[] words) {
		Indexer<String> indexer = new Indexer<String>();
		for (String word : words) {
			for (int i = 0; i <= word.length(); i++)
				indexer.get(word.substring(0, i));
		}
		return indexer;
	}
}

class InputReader_egor {

	private InputStream stream;
	private byte[] buf = new byte[1024];
	private int curChar;
	private int numChars;
	private SpaceCharFilter filter;

	public InputReader_egor(InputStream stream) {
		this.stream = stream;
	}

	public int read() {
		if (numChars == -1)
			throw new InputMismatchException();
		if (curChar >= numChars) {
			curChar = 0;
			try {
				numChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (numChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public int readInt() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			if (c < '0' || c > '9')
				throw new InputMismatchException();
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public String readString() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		StringBuilder res = new StringBuilder();
		do {
			res.appendCodePoint(c);
			c = read();
		} while (!isSpaceChar(c));
		return res.toString();
	}

	public boolean isSpaceChar(int c) {
		if (filter != null)
			return filter.isSpaceChar(c);
		return isWhitespace(c);
	}

	public static boolean isWhitespace(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	public interface SpaceCharFilter {
		public boolean isSpaceChar(int ch);
	}
}

class OutputWriter {
	private final PrintWriter writer;

	public OutputWriter(OutputStream outputStream) {
		writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
	}

	public OutputWriter(Writer writer) {
		this.writer = new PrintWriter(writer);
	}

	public void print(Object...objects) {
		for (int i = 0; i < objects.length; i++) {
			if (i != 0)
				writer.print(' ');
			writer.print(objects[i]);
		}
	}

    public void printLine(Object...objects) {
		print(objects);
		writer.println();
	}

	public void close() {
		writer.close();
	}

	public void printLine(int i) {
		writer.println(i);
	}
}

class IOUtils {

	public static String[] readStringArray(InputReader_egor in, int size) {
		String[] array = new String[size];
		for (int i = 0; i < size; i++)
			array[i] = in.readString();
		return array;
	}

	}

class Indexer<K> extends EHashMap<K, Integer> {
	private int index = 0;

	public Integer get(Object key) {
		if (!containsKey(key))
			put((K) key, index++);
		return super.get(key);
	}
}

class Graph {
	public static final int REMOVED_BIT = 0;

	protected int vertexCount;
	protected int edgeCount;

	private int[] firstOutbound;
	private int[] firstInbound;

	private Edge[] edges;
	private int[] nextInbound;
	private int[] nextOutbound;
	private int[] from;
	private int[] to;
	private long[] weight;
	private long[] capacity;
	private int[] reverseEdge;
	private int[] flags;

	public Graph(int vertexCount) {
		this(vertexCount, vertexCount);
	}

	public Graph(int vertexCount, int edgeCapacity) {
		this.vertexCount = vertexCount;
		firstOutbound = new int[vertexCount];
		Arrays.fill(firstOutbound, -1);

		from = new int[edgeCapacity];
		to = new int[edgeCapacity];
		nextOutbound = new int[edgeCapacity];
		flags = new int[edgeCapacity];
	}

	public int addEdge(int fromID, int toID, long weight, long capacity, int reverseEdge) {
		ensureEdgeCapacity(edgeCount + 1);
		if (firstOutbound[fromID] != -1)
			nextOutbound[edgeCount] = firstOutbound[fromID];
		else
			nextOutbound[edgeCount] = -1;
		firstOutbound[fromID] = edgeCount;
		if (firstInbound != null) {
			if (firstInbound[toID] != -1)
				nextInbound[edgeCount] = firstInbound[toID];
			else
				nextInbound[edgeCount] = -1;
			firstInbound[toID] = edgeCount;
		}
		this.from[edgeCount] = fromID;
		this.to[edgeCount] = toID;
		if (capacity != 0) {
			if (this.capacity == null)
				this.capacity = new long[from.length];
			this.capacity[edgeCount] = capacity;
		}
		if (weight != 0) {
			if (this.weight == null)
				this.weight = new long[from.length];
			this.weight[edgeCount] = weight;
		}
		if (reverseEdge != -1) {
			if (this.reverseEdge == null) {
				this.reverseEdge = new int[from.length];
				Arrays.fill(this.reverseEdge, 0, edgeCount, -1);
			}
			this.reverseEdge[edgeCount] = reverseEdge;
		}
		if (edges != null)
			edges[edgeCount] = createEdge(edgeCount);
		return edgeCount++;
	}

	protected final GraphEdge createEdge(int id) {
		return new GraphEdge(id);
	}

	public final int addFlowWeightedEdge(int from, int to, long weight, long capacity) {
		if (capacity == 0) {
			return addEdge(from, to, weight, 0, -1);
		} else {
			int lastEdgeCount = edgeCount;
			addEdge(to, from, -weight, 0, lastEdgeCount + entriesPerEdge());
			return addEdge(from, to, weight, capacity, lastEdgeCount);
		}
	}

	protected int entriesPerEdge() {
		return 1;
	}

	public final int addWeightedEdge(int from, int to, long weight) {
		return addFlowWeightedEdge(from, to, weight, 0);
	}

	public final int addSimpleEdge(int from, int to) {
		return addWeightedEdge(from, to, 0);
	}

	public final int vertexCount() {
		return vertexCount;
	}

	public final int edgeCount() {
		return edgeCount;
	}

	public final int firstInbound(int vertex) {
		initInbound();
		int id = firstInbound[vertex];
		while (id != -1 && isRemoved(id))
			id = nextInbound[id];
		return id;
	}

	public final int nextInbound(int id) {
		initInbound();
		id = nextInbound[id];
		while (id != -1 && isRemoved(id))
			id = nextInbound[id];
		return id;
	}

	public final int source(int id) {
		return from[id];
	}

	public final int destination(int id) {
		return to[id];
	}

	public final long weight(int id) {
		if (weight == null)
			return 0;
		return weight[id];
	}

	private void initInbound() {
		if (firstInbound == null) {
			firstInbound = new int[firstOutbound.length];
			Arrays.fill(firstInbound, 0, vertexCount, -1);
			nextInbound = new int[from.length];
			for (int i = 0; i < edgeCount; i++) {
				nextInbound[i] = firstInbound[to[i]];
				firstInbound[to[i]] = i;
			}
		}
	}

	public final boolean flag(int id, int bit) {
		return (flags[id] >> bit & 1) != 0;
	}

	public final boolean isRemoved(int id) {
		return flag(id, REMOVED_BIT);
	}

	protected void ensureEdgeCapacity(int size) {
		if (from.length < size) {
			int newSize = Math.max(size, 2 * from.length);
			if (edges != null)
				edges = resize(edges, newSize);
			from = resize(from, newSize);
			to = resize(to, newSize);
			nextOutbound = resize(nextOutbound, newSize);
			if (nextInbound != null)
				nextInbound = resize(nextInbound, newSize);
			if (weight != null)
				weight = resize(weight, newSize);
			if (capacity != null)
				capacity = resize(capacity, newSize);
			if (reverseEdge != null)
				reverseEdge = resize(reverseEdge, newSize);
			flags = resize(flags, newSize);
		}
	}

	protected final int[] resize(int[] array, int size) {
		int[] newArray = new int[size];
		System.arraycopy(array, 0, newArray, 0, array.length);
		return newArray;
	}

	private long[] resize(long[] array, int size) {
		long[] newArray = new long[size];
		System.arraycopy(array, 0, newArray, 0, array.length);
		return newArray;
	}

	private Edge[] resize(Edge[] array, int size) {
		Edge[] newArray = new Edge[size];
		System.arraycopy(array, 0, newArray, 0, array.length);
		return newArray;
	}

	protected class GraphEdge implements Edge {
		protected int id;

		protected GraphEdge(int id) {
			this.id = id;
		}

		}

	}

interface IndependentSetSystem {
	public boolean join(int first, int second);

	public int get(int index);

	public static interface Listener {
		public void joined(int joinedRoot, int root);
	}
}

class RecursiveIndependentSetSystem implements IndependentSetSystem {
	private final int[] color;
	private final int[] rank;
	private int setCount;
	private Listener listener;

	public RecursiveIndependentSetSystem(int size) {
		color = new int[size];
		rank = new int[size];
		for (int i = 0; i < size; i++)
			color[i] = i;
		setCount = size;
	}

	public RecursiveIndependentSetSystem(RecursiveIndependentSetSystem other) {
		color = other.color.clone();
		rank = other.rank.clone();
		setCount = other.setCount;
	}

	public boolean join(int first, int second) {
		first = get(first);
		second = get(second);
		if (first == second)
			return false;
		if (rank[first] < rank[second]) {
			int temp = first;
			first = second;
			second = temp;
		} else if (rank[first] == rank[second])
			rank[first]++;
		setCount--;
		color[second] = first;
		if (listener != null)
			listener.joined(second, first);
		return true;
	}

	public int get(int index) {
		if (color[index] == index)
			return index;
		return color[index] = get(color[index]);
	}

	}

class EHashMap<E, V> extends AbstractMap<E, V> {
	private static final int[] shifts = new int[10];

	private int size;
	private HashEntry<E, V>[] data;
	private int capacity;
	private Set<Entry<E, V>> entrySet;

	static {
		Random random = new Random(System.currentTimeMillis());
		for (int i = 0; i < 10; i++)
			shifts[i] = 1 + 3 * i + random.nextInt(3);
	}

	public EHashMap() {
		this(4);
	}

	private void setCapacity(int size) {
		capacity = Integer.highestOneBit(4 * size);
        //noinspection unchecked
        data = new HashEntry[capacity];
	}

	public EHashMap(int maxSize) {
		setCapacity(maxSize);
		entrySet = new AbstractSet<Entry<E, V>>() {
			@Override
			public Iterator<Entry<E, V>> iterator() {
				return new Iterator<Entry<E, V>>() {
					private HashEntry<E, V> last = null;
                    private HashEntry<E, V> current = null;
                    private HashEntry<E, V> base = null;
                    private int lastIndex = -1;
					private int index = -1;

					public boolean hasNext() {
                        if (current == null) {
                            for (index++; index < capacity; index++) {
                                if (data[index] != null) {
                                    base = current = data[index];
                                    break;
                                }
                            }
                        }
                        return current != null;
					}

					public Entry<E, V> next() {
						if (!hasNext())
							throw new NoSuchElementException();
                        last = current;
                        lastIndex = index;
                        current = current.next;
                        if (base.next != last)
                            base = base.next;
						return last;
					}

					public void remove() {
						if (last == null)
							throw new IllegalStateException();
                        size--;
                        if (base == last)
                            data[lastIndex] = last.next;
                        else
                            base.next = last.next;
					}
				};
			}

			@Override
			public int size() {
				return size;
			}
		};
	}

	public EHashMap(Map<E, V> map) {
		this(map.size());
		putAll(map);
	}

	public Set<Entry<E, V>> entrySet() {
		return entrySet;
	}

	public void clear() {
		Arrays.fill(data, null);
		size = 0;
	}

	private int index(Object o) {
		return getHash(o.hashCode()) & (capacity - 1);
	}

	private int getHash(int h) {
		int result = h;
		for (int i : shifts)
			result ^= h >>> i;
		return result;
	}

	public V remove(Object o) {
		if (o == null)
			return null;
		int index = index(o);
        HashEntry<E, V> current = data[index];
        HashEntry<E, V> last = null;
        while (current != null) {
            if (current.key.equals(o)) {
                if (last == null)
                    data[index] = current.next;
                else
                    last.next = current.next;
                size--;
                return current.value;
            }
            last = current;
            current = current.next;
        }
        return null;
	}

	public V put(E e, V value) {
		if (e == null)
			return null;
		int index = index(e);
        HashEntry<E, V> current = data[index];
        if (current != null) {
            while (true) {
                if (current.key.equals(e)) {
                    V oldValue = current.value;
                    current.value = value;
                    return oldValue;
                }
                if (current.next == null)
                    break;
                current = current.next;
            }
        }
        if (current == null)
            data[index] = new HashEntry<E, V>(e, value);
        else
            current.next = new HashEntry<E, V>(e, value);
        size++;
        if (2 * size > capacity) {
            HashEntry<E, V>[] oldData = data;
            setCapacity(size);
            for (HashEntry<E, V> entry : oldData) {
                while (entry != null) {
                    HashEntry<E, V> next = entry.next;
                    index = index(entry.key);
                    entry.next = data[index];
                    data[index] = entry;
                    entry = next;
                }
            }
        }
		return null;
	}

	public V get(Object o) {
		if (o == null)
			return null;
		int index = index(o);
        HashEntry<E, V> current = data[index];
		while (current != null) {
			if (current.key.equals(o))
                return current.value;
            current = current.next;
        }
		return null;
	}

	public boolean containsKey(Object o) {
        if (o == null)
            return false;
        int index = index(o);
        HashEntry<E, V> current = data[index];
        while (current != null) {
            if (current.key.equals(o))
                return true;
            current = current.next;
        }
        return false;
	}

	public int size() {
		return size;
	}

	private static class HashEntry<E, V> implements Entry<E, V> {
		private final E key;
		private V value;
        private HashEntry<E, V> next;

        private HashEntry(E key, V value) {
            this.key = key;
            this.value = value;
        }


        public E getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
    }
}

interface Edge {
	}

