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 C++ (judge verdict: Accepted)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <map>
using namespace std;
 
typedef pair<string,int> P;
 
int n;
string str[11];
string INF;
 
map<P,string> m;

int check(string s){
  int ret = 0;
  for(int i = 0; i < n; i++){
    if((int)s.find(str[i]) != -1){
      ret |= 1<<i; 
    }
  }
  return ret;
}

string solve(string sss,int used){
  if(m[P(sss,used)] != "") return m[P(sss,used)];
 
  if(used == (1<<n)-1) return "";
 
  string ret = INF;
  string temp = "";
  int c;
 
  for(int i = 0; i < n; i++){
    if( (used & 1<<i) == 0){
      /*
	if((int)sss.find(str[i]) != -1){
	//ret = min(ret, solve(sss,used|1<<i));
	temp = sss + solve(sss,used|1<<i);
	ret = (ret.size() < temp.size())?ret:temp;
	}else{
      */
      for(int j = 1; j < (int)(min(sss.size(),str[i].size()));j++){
	if(sss.substr(sss.size() - j) == str[i].substr(0,j)){
	  //ret = min(ret, solve(sss + str[i].substr(j),used|1<<i) + (int)str[i].size() - j);

	  string t = sss + str[i].substr(j);
	  c = check(t);
	  temp = str[i].substr(j) + solve(t.substr(max((int)t.size()-10,0)),(used|1<<i)|c);
	  if(ret.size() == temp.size()){
	    ret = (ret < temp)?ret:temp;
	  }else{
	    ret = (ret.size() < temp.size())?ret:temp;
	  }
	}
      }
      //ret = min(ret, solve(sss + str[i],used|1<<i) + (int)str[i].size());

      string t = sss + str[i];
      c = check(t);
      temp = str[i] + solve(t.substr(max((int)t.size()-10,0)),(used|1<<i)|c);	  
      if(ret.size() == temp.size()){
	ret = (ret < temp)?ret:temp;
      }else{
	ret = (ret.size() < temp.size())?ret:temp;
      }
      //}
    }
  }
  //cout<< ret << endl;
  return m[P(sss,used)] = ret;
  //return m[P(sss,used)] = ret.substr((int)sss.size());
}
 
int main(void)
{
  for(int i = 0;i < 111; i++) INF += "A";
 
  while(1){
    cin >> n;
    if(!n)break;
    for(int i = 0; i < n; i++){
      cin >> str[i];
    }
    map<P,string> a;
    m = a;
 
    cout << solve("",0) << endl;
  }
}