using namespace std;
#include<cstdio>
#include<vector>
#include<algorithm>
#include<string.h>
#define MAX_N 18
#define PN (1<<N)
#define MAX_S 30002
#define bit(x) (1<<(x-1))
#define pb push_back
#define mk make_pair
#define f first
#define s second
#define Inf 0x3f3f3f3f
#define Mod1 666013
#define Mod2 100007
#define B1 26
#define B2 71
char M[MAX_N+2][MAX_S];
char X[MAX_N+2][MAX_S];
int N, P;
int dp[(1<<18)+4][MAX_N];
short int pre[(1<<18)+4][MAX_N];
vector<pair<int,int> >G[MAX_N+2];
int pref[MAX_S];
int C[MAX_N+2][MAX_N+2];
inline void prefix(char s[], int N)
{
int i,k=0;
pref[1] = 0;
for(i=2;i<=N;++i)
{
while(k && s[i] != s[k+1]) k = pref[k];
if(s[i] == s[k+1]) ++k;
pref[i] = k;
}
}
inline int KMP(char a[], int N, char b[], int M) // returneaza 1, daca b[] e inclus in a[]
{
memset(pref,0,sizeof(pref));
// b[] e modelul
prefix(b,M);
int i,k=0;
for(i=1;i<=N;++i)
{
while(k && a[i] != b[k+1]) k = pref[k];
if(a[i] == b[k+1]) ++k;
if(k == M) return 1; // B[] este inclus in A[]
}
return 0; // B[] nu este inclus in A[]
}
inline int cost(char a[], int N, char b[], int M)
{
//returnez lungimea celui mai lung prefix a lui B care este sufix a lui A
int P1=1, P2=1, sol = 0;
int hash1a, hash2a, hash1b, hash2b;
hash1a = hash2a = hash1b = hash2b = 0;
int i,j;
for(i = N, j = 1; j<=M && i>0; --i,++j)
{
hash1a += (a[i] * P1) % Mod1; hash1a %= Mod1;
hash2a += (a[i] * P2) % Mod2; hash2a %= Mod2;
hash1b = (hash1b * B1) % Mod1;
hash1b = (hash1b + b[j]) % Mod1;
hash2b = (hash2b * B2) % Mod2;
hash2b = (hash2b + b[j]) % Mod2;
if(hash1a == hash1b && hash2a == hash2b) sol = j;
P1 = (P1 * B1) % Mod1; P2 = (P2*B2)%Mod2;
}
return sol;
}
void read()
{
freopen("adn.in","r",stdin);
freopen("adn.out","w",stdout);
scanf("%d\n",&P);
int i;
for(i=1;i<=P;++i)
{
scanf("%s\n",M[i]+1);
M[i][0] = '!';
}
}
void eliminare() // elimin cuvintele incluse unul in altul
{
int i,j,n,m;
for(i=1;i<=P;++i)
for(j=1, n=strlen(M[i])-1;j<=P && M[i][0]=='!';++j)
if(i!=j && M[j][0] == '!') //daca i e inclus in j
{
m = strlen(M[j])-1;
if(n > m) continue;
if(KMP(M[j],m,M[i],n)) M[i][0] = '*';
}
N = 0;
for(i=1;i<=P;++i)
if(M[i][0] != '*')
{
n = strlen(M[i])-1;
++N;
for(j=0;j<=n;++j) X[N][j] = M[i][j];
}
}
void construire()
{
// construiesc graful .
// i -> j, de cost c <=> sufixul lui i este prefix a lui j, de lg c
int i,j, cst,n,m;
for(i=1;i<=N;++i)
for(n = strlen(X[i])-1,j=1;j<=N;++j)
if(i!=j)
{
m = strlen(X[j])-1;
cst = cost(X[i],n,X[j],m);
G[i].pb(mk(j,cst));
C[i][j] = cst;
}
}
int sol[20], nr;
void recon(int i, int j)
{
if(pre[i][j] != j) recon(i - bit(j), pre[i][j]);
sol[++nr] = j;
}
void dinamica()
{
int i,j,k,y, p;
for(i=1;i<=(1<<N);++i) for(j=1;j<=N;++j) dp[i][j] = -Inf;
for(i=1;i<=N;++i) { dp[bit(i)][i] = 0; pre[bit(i)][i] = i; }
for(i=1;i<=PN;++i)
for(j=1;j<=N;++j)
{
if(dp[i][j] == -Inf) continue;
for(k=0;k<G[j].size();++k)
{
y = G[j][k].f;
if(bit(y) & i) continue;
if(bit(y) + i > PN) continue;
if(dp[i][j] + G[j][k].s <= dp[i+bit(y)][y]) continue;
dp[i+bit(y)][y] = dp[i][j] + G[j][k].s;
pre[i+bit(y)][y] = j;
}
}
p = -1;
for(i=1;i<=N;++i) if(p == -1 || dp[PN-1][i] > dp[PN-1][p]) p = i;
recon((1<<N)-1,p);
}
/*void debugsol()
{
int i;
for(i=1;i<=nr;++i) printf("%d ",sol[i]);
}
void deb()
{
int i;
for(i=1;i<=N;++i)
printf("%s\n",X[i]+1);
}*/
void det_sol()
{
int i;
printf("%s",X[sol[1]]+1);
for(i=2;i<=nr;++i)
printf("%s",X[sol[i]] + 1+C[sol[i-1]][sol[i]]);
}
int main()
{
read();
eliminare();
construire();
dinamica();
det_sol();
return 0;
}