import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; import java.util.LinkedList; import java.util.HashSet; import java.util.Iterator; import java.util.Collection; // This problem is really just an application of breadth-first search, // since the lengths of the student IDs are capped (at 15). //////////////////////////////////////// // Simple queue to manage our queue of candidate solutions. Each is // an array of indices, but since the indices are small, we use bytes. class AbbrevQueue extends LinkedList { AbbrevQueue(){ super(); } void enqueue(byte[] s){ addLast(s); } void enqueue(Collection c){ Iterator iter = c.iterator(); while (iter.hasNext()){ enqueue((byte[])iter.next()); } } byte[] dequeue(){ return (byte[])removeFirst(); } } //////////////////////////////////////// // class that does the main work public class UniqueIDs { static List ids; // holds all the IDs static int idLength; // convenient to have this available public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int numCases = Integer.parseInt(in.readLine()); for (int i = 0; i < numCases; i++){ processTestCase(in); } } // reads in a newline delimited list of IDs, and finds a subsequence // of index positions that uniquely determines each ID in the list. // Solution is to use brute-force breadth-first search. static void processTestCase(BufferedReader in) throws Exception { ids = new LinkedList(); // read in all the data int numIDs = Integer.parseInt(in.readLine()); for (int i = 0; i < numIDs; i++){ UniqueIDs.ids.add(in.readLine()); } UniqueIDs.idLength = ((String)UniqueIDs.ids.get(0)).length(); // initialize our BFS with all the length-1 abbreviations AbbrevQueue q = new AbbrevQueue(); for (int i = 0; i < UniqueIDs.idLength; i++){ byte[] tmp = {(byte)i}; q.enqueue(tmp); } // run the BFS while (!q.isEmpty()){ // should never be empty in this problem space byte[] node = q.dequeue(); if (success(node)){ printAbbrev(node); return; } q.enqueue(successors(node)); } } // evaluate whether or not the candidate abbreviation template // identifies a distinct set of abbreviations static boolean success(byte[] candidate){ HashSet abbrevs = new HashSet(); // we could est. the max capacity Iterator iter = UniqueIDs.ids.iterator(); while (iter.hasNext()){ String id = (String)iter.next(); String abbrev = constructAbbrev(candidate, id); if (abbrevs.contains(abbrev)) return false; abbrevs.add(abbrev); } return true; } // basically maps the template over the actual ID to get the // abbreviation static String constructAbbrev(byte[] template, String id){ StringBuffer abbrev = new StringBuffer(template.length); for (int i = 0; i < template.length; i++){ abbrev.append(id.charAt(template[i])); } return abbrev.toString(); } // given a node in the BFS (a candidate abbreviation), return a // list of the nodes (candidate abbrevs) that expand from this one // // they'll be constructed in lexicographic order, so we don't have // to worry about dupes. static Collection successors(byte[] node){ Collection c = new LinkedList(); int lastIndex = node[node.length-1]; byte[] newNode; for (int i = lastIndex + 1; i < UniqueIDs.idLength; i++){ newNode = new byte[node.length+1]; System.arraycopy(node, 0, newNode, 0, node.length); newNode[node.length] = (byte)i; c.add(newNode); } return c; } // to cleanly print out our answer static void printAbbrev(byte[] node){ for (int i = 0; i < node.length; i++){ System.out.print((int)node[i]); System.out.print(' '); } System.out.println(); } }