Problem:
Princess, a Cryptanalyst

お姫様の暗号解読

English text is not available in this practice contest.

ある貧乏な国のおてんばで勇敢なお姫様は，お忍びで出かけた町である日本語で書かれた古文書を手に入れた．お姫様は日本語ができるため，さっそくこの古文書を読んでみた．すると，驚くべき事が分かった．この古文書は，古の秘宝の在処を示していたのである．ところが，秘宝の在処は暗号になっており，容易には分からないようになっていた．そこでお姫様は，従者であるあなたに暗号の解読を手伝うように命令を下した．

あなたはお姫様を助けるため昼夜を問わず調査を行った．その結果，Shortest Secret String (SSS) と呼ばれる文字列が解読に重要な役割を果たすことがわかった．ここで SSS とは，N 個の単語全部を部分文字列として含み，なおかつその長さが最小であるような文字列である．

あなたの仕事は，N 個の単語から SSS を見つけ出す事である．

Input

入力は複数のデータセットで与えられる．データセットの初めの一行は，データセットに含まれる単語の数N(1≦N≦10)が与えられる．続くN行に単語が与えられる．最後のデータセットの後に，0のみを含む一行が与えられる．

なお，入力で与えられる単語はアルファベット小文字のみからなり，長さは高々10であることが保証されている．

Output

各データセットについてSSS を一行に出力せよ．また，複数ある場合は辞書式順序で最小のものを出力せよ．

Sample Input

4
apple
length
things
thin
2
icp
cpc
3
zeta
eta
alphabet
2
until
till
0

Output for the Sample Input

applengthings
icpc
zetalphabet
untill

Solution in JAVA (judge verdict: Accepted)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {
	
	public static String connect(String pre, String post){
		char[] pre_array = pre.toCharArray();
		char[] post_array = post.toCharArray();
		
		int min = Math.min(pre_array.length, post_array.length);
		
		int over = 0;
		for(int range = 0; range <= min; range++){
			boolean flag = true;
			for(int pos = 0; pos < range; pos++){
				//System.out.println(pre_array[pre_array.length - (range - pos)] + " " + post_array[pos]);
				
				if(pre_array[pre_array.length - (range - pos)]
					!= post_array[pos]){
						flag = false;
						break;
					}
			}
			
			//System.out.println(range);
			
			if(flag){
				over = Math.max(over, range);
			}
		}
		
		//System.out.println(pre+ " " + post.substring(over));
		
		return pre.concat(post.substring(over));
	}
	
	public static final int MAX = 10;
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		String[][] dp = new String[1 << MAX][MAX];
		
		while (true) {
			final int N = sc.nextInt();

			if (N == 0) {
				break;
			}
			
			ArrayList<String> array_list = new ArrayList<String>();
			
			for (int i = 0; i < N; i++) {
				array_list.add(sc.next());
			}
			
			ArrayList<String> no_dup_list = new ArrayList<String>();
			for(int i = 0; i < N; i++){
				String left = array_list.get(i);
				boolean flag = false;
				
				for(int j = 0; j < N; j++){	
					if(i == j){
						continue;
					}
					
					String right = array_list.get(j);
					
					if(right.indexOf(left) > 0){
						flag = true;
						break;
					}else if(right.indexOf(left) == 0 && no_dup_list.contains(left)){
						flag = true;
						break;
					}
				}
				
				if(!flag){
					no_dup_list.add(left);
				}
			}
			
			//System.out.println(no_dup_list);
			
			String[] array = new String[no_dup_list.size()];
			for(int i = 0; i < no_dup_list.size(); i++){
				array[i] = no_dup_list.get(i);
			}
			
			Arrays.sort(array);
			//System.out.println(Arrays.toString(array));
			
			final int n = array.length;
			
			final int limit = 1 << n;
			
			for(int S = 0; S < limit; S++){
				for(int i = 0; i < n; i++){
					dp[S][i] = null;
				}
			}
			
			for(int i = 0; i < n; i++){
				dp[0][i] = "";
				dp[1 << i][i] = array[i];
			}
			
			for(int S = 0; S < limit; S++){
				for(int i = 0; i < n; i++){
					if((S & (1 << i)) == 0){
						continue;
					}else if(dp[S][i] == null){
						continue;
					}
					
					for(int j = 0; j < n; j++){
						if((S & (1 << j)) != 0){
							continue;
						}
						
						//System.out.println("[" + Integer.toBinaryString(S) + " " + i + "] " + dp[S][i] + " " + array[j]);
						final String consist = connect(dp[S][i], array[j]);
						
						if(dp[S | (1 << j)][j] == null ||
								dp[S | (1 << j)][j].length() > consist.length()){
							dp[S | (1 << j)][j] = consist;
						}else if(dp[S | (1 << j)][j].length() == consist.length()){
							if(dp[S | (1 << j)][j].compareTo(consist) > 0){
								 dp[S | (1 << j)][j] = consist;
							}
						}
					}
				}
			}
			
			String min_string = null;
			for(int i = 0; i < n; i++){
				if(min_string == null){
					min_string = dp[limit-1][i];
				}else if(min_string.length() > dp[limit - 1][i].length()){
					min_string = dp[limit-1][i];
				}else if(min_string.length() == dp[limit - 1][i].length()){
					if(min_string.compareTo(dp[limit - 1][i]) > 0){
						min_string = dp[limit-1][i];
					}
				}
			}
			
			System.out.println(min_string);
		}

		sc.close();
	}

}