#include <cstring>
#include <cstdio>
#include <string>
#include <fstream>
#define maxlen 30001
#define inf 0x3f3f3f3f
using namespace std;
int N,used;
int length[19];
char s[19][maxlen];
int prefix[maxlen];
int pref[19][19];
int best[1 << 18][19]; //best[i][j] = costul unui lant hamiltonian de configuratie i si care il contine pe j
int pred[1 << 18][19]; //pred[i][j] = nodul predecesor nodului j din cel mai scurt lant hamiltonian cu configuratia i
void make_prefix(int l2){
//aceasta functie calculeaza vectorul prefix pentru KMP
//prefix[i] = cel mai lung prefix al lui s[l2] care este sufix al lui s[l2]i
int k = 0,i;
prefix[0] = 1;
for(i = 2; s[l2][i]; ++i){
while(k != 0 && s[l2][k + 1] != s[l2][i])k = prefix[k];
if(s[l2][i] == s[l2][i])k++;
prefix[i] = k;
}
}
int kmp(int l1,int l2){
//
int k = 0,i,j;
k = 0;
for(i = 1; s[l1][i]; ++i){
while(k != 0 && s[l1][i] != s[l2][k + 1])
k = prefix[k];
if(s[l1][i] == s[l2][k + 1])k++;
if(s[l2][k + 1] == '\0')return k;
}
return k;
}
int main(){
ifstream fin("adn.in");
ofstream fout("adn.out");
//citire:
(fin >> N).ignore();
int i,j,ret,k;
for(i = 0;i < N;++i){
fin.getline(s[i] + 1,maxlen);
length[i] = strlen(s[i] + 1);
}
//elimiarea sirurilor:
for(i = 0;i < N;++i){
make_prefix(i);
for(j = 0;j < N;++j)
if(i != j){
ret = kmp(j,i);
if(ret == length[i]){
used |= 1 << i; // used = reprezentarea binara a cuvintelor folosite ( bitul i este 1 daca cuvantul s[i] se
// gaseste in alt cuvant)
break;
}
else pref[j][i] = length[i] - ret; //cel mai lung sufix al cuvantului s[i]
//care este prefix al cuvantului s[j]
}
}
for(i = 1;i < (1 << N);++i)
for(j = 0;j < N;++j)best[i][j] = inf;
for(i = 0;i < N;++i)
best[1 << i][i] = length[i];
for(i = 1;i < (1 << N);++i)
if((i & used) == 0 && (i & (i - 1))){ //daca cuvantul i nu se afla in alt cuvant si daca i nu este putere a lui 2
for(j = 0;j < N;++j)
if(i & (1 << j)) // daca bitul j din reprezentarea pe biti a lui i este 1
{
for(k = 0;k < N;++k)
if(j != k && (i & (1 << k)) && best[i][j] > best[i ^ (1 << j)][k] + pref[k][j]){
best[i][j] = best[i ^ (1 << j)][k] + pref[k][j];
pred[i][j] = k;
}
}
}
int mask = ((1 << N) - 1) ^ used; // mask = toate cuvintele care nu sunt in used
//se identifica un k astfel incat best[mask][k] sa fie minim (cel mai scurt lant hamiltonian)
k = -1;
for(j = 0;j < N;++j){
if((used & (1 << j)) == 0) // daca cuvantul s[j] apare in alt cuvant
if(k == -1 || best[mask][j] < best[mask][k])
k = j;
}
string R;
for(i = mask; i; k = j){
j = pred[i][k];
i ^= 1 << k; // se elimina nodul k din lant
if(i) R.insert(R.begin(), s[k] + length[k] + 1 - pref[j][k], s[k] + length[k] + 1);
else R.insert(R.begin(), s[k] + 1, s[k] + length[k] + 1);
}
fout << R << "\n";
return 0;
}