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.*;
import static java.lang.Math.*;
public class Main {
	final Scanner sc=new Scanner(System.in);
	public static void main(String[] args) {
		new Main().init();
	}
	void init(){
		final long STACK_SIZE=8*1024*1024;
		new Thread(null, new AOJ2022(), "RUN", STACK_SIZE).start();
	}
	
	class AOJ2022 implements Runnable{
		AOJ2022(){}
		@Override public void run(){
			while(sc.hasNext()){
				N=sc.nextInt();
				if(N==0)	break;
				solve();
			}
		}
		
		int N,N2;
		
		void solve(){
			String[] str=new String[N];
			for(int i=0; i<N; ++i)	str[i]=sc.next();
			
			boolean[] eq=new boolean[N];
			for(int i=0; i<N; ++i){
				if(eq[i])	continue;
				for(int j=0; j<N; ++j){
					if(i!=j && !eq[j] && str[i].matches(".*?"+str[j]+".*?"))	eq[j]=true;
				}
			}
			ArrayList<String> str2=new ArrayList<String>();
			for(int i=0; i<N; ++i)if(!eq[i])	str2.add(str[i]);
			N2=str2.size();
			
//			System.out.println(str2);
			
			int[][] len=new int[N2][N2];
			for(int i=0; i<N2; ++i){
				for(int j=0; j<N2; ++j){
					if(i==j)	continue;
					int offset=0, l=str2.get(i).length();
					for( ; offset<l; ++offset){
						if( str2.get(j).startsWith( str2.get(i).substring(offset) ) )	break;
					}
					
					len[i][j]=l-offset;
				}
			}
			
//			for(int j=0; j<N2; ++j){
//				for(int i=0; i<N2; ++i)	System.out.print(len[i][j]+" | ");
//				System.out.println();
//			}
			
			StringBuilder[][] dp=new StringBuilder[1<<N2][N2];
			for(int i=0; i<N2; ++i){
				dp[1<<i][i]=new StringBuilder(str2.get(i));
			}
			for(int i=0; i<(1<<N2); ++i){
				for(int j=0; j<N2; ++j){
					if(dp[i][j]==null)	continue;
					for(int k=0; k<N2; ++k){
						if( (i&(1<<k)) > 0 )	continue;
						StringBuilder next=new StringBuilder(dp[i][j]);
						next.append(str2.get(k).substring(len[j][k]));
						int newIdx=i|(1<<k);
						int len1=dp[newIdx][k]==null? 1<<29 : dp[newIdx][k].length(), len2=next.length();
						if(len1>len2 || ( len1==len2 && dp[newIdx][k].toString().compareTo(next.toString()) > 0 ) ){
							dp[newIdx][k]=next;
						}
					}
				}
			}
			
//			for(int j=0; j<N2; ++j){
//				for(int i=0; i<(1<<N2); ++i)	System.out.print(dp[i][j]+" | ");
//				System.out.println();
//			}
			
			String ans=null;
			int tmp=(1<<N2)-1;
			for(int i=0; i<N2; ++i){
				if(dp[tmp][i]!=null && ( ans==null || ans.length() > dp[tmp][i].length() || ( ans.length()==dp[tmp][i].length() && ans.compareTo(dp[tmp][i].toString()) > 0 ) ) ){
					ans=dp[tmp][i].toString();
				}
			}
			System.out.println(ans);
			
		}
	}
}