#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
void make_prefix(const string& s, vector<int>& v){
const int n = s.size();
v.resize(n, 0);
for(int i = 1; i < n; ++i){
int j = v[i-1];
for( ;
j > 0 && s[j] != s[i];
j = v[j-1]);
if(s[j] == s[i]){
++j; }
v[i] = j; } }
int longest_common_pref_suf(const string& a, const string& b){
const string s = b + "#" + a;
vector<int> v;
make_prefix(s, v);
return v.back(); }
bool apartine(const string& mic, const string& mare){
const string s = mic + "#" + mare;
vector<int> v;
make_prefix(s, v);
return any_of(begin(v)+2*mic.size()+1, end(v), [mic](const int x){
return x == mic.size(); }); }
void make_graf(const vector<string>& v, vector<vector<int> >& graf){
const int n = v.size();
graf.resize(n, vector<int>(n, 0));
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
if(i != j){
const int len = v[j].size() - longest_common_pref_suf(v[i], v[j]);
graf[i][j] = len; } } } }
void backtrack(const vector<vector<int> >& graf, vector<vector<int> >& cost,
vector<vector<int> >& prev, vector<int>& cur, const int cod_cur, const int sz){
if(sz == cur.size()){
for(const auto x : cur){
for(const auto y : cur){
if(y != x && cost[x][cod_cur] > cost[y][cod_cur ^ (1<<x)] + graf[y][x]){
cost[x][cod_cur] = cost[y][cod_cur ^ (1<<x)] + graf[y][x];
prev[x][cod_cur] = y; } } } }
else{
for(int next = cur.back() + 1; next < graf.size(); ++next){
cur.push_back(next);
backtrack(graf, cost, prev, cur, cod_cur | (1<<next), sz);
cur.pop_back(); } } }
void hamiltonian_path(const vector<vector<int> >& graf, const int st, vector<int>& rez){
const int n = graf.size();
vector<vector<int> > cost(n, vector<int>(1<<n, 1e8)),
prev(n, vector<int>(1<<n, 0));
cost[st][1<<st] = 0;
prev[st][1<<st] = st;
vector<int> cur = {st};
for(int sz = 2; sz <= n; ++sz){
backtrack(graf, cost, prev, cur, 1<<st, sz); }
const int all_one = (1<<n) - 1;
int fin_coord = -1, cost_min = 1e9;
for(int i = 0; i < n; ++i){
if(cost_min > cost[i][all_one]){
cost_min = cost[i][all_one];
fin_coord = i; } }
for(int i = fin_coord, cod = all_one; i != st; ){
rez.push_back(i);
const int cod_n = cod ^ (1<<i);
i = prev[i][cod];
cod = cod_n; }
rez.push_back(st);
reverse(begin(rez), end(rez)); }
void citeste_strings(ifstream& f, vector<string>& v){
int n = 0;
f >> n;
v.resize(n+1);
for(int i = 1; i <= n; ++i){
f >> v[i]; }
vector<int> to_erase;
for(int i = 1; i < v.size(); ++i){
for(int j = 1; j < v.size(); ++j){
if(i != j && v[i].size() <= v[j].size() && apartine(v[i], v[j])){
to_erase.push_back(i); }
if(i != j && v[i] == v[j]){
to_erase.push_back(min(i, j)); } } }
sort(begin(to_erase), end(to_erase), greater<int>());
to_erase.erase(unique(begin(to_erase), end(to_erase)), end(to_erase));
for(const auto x : to_erase){
v.erase(begin(v) + x); } }
int main(){
ifstream f("adn.in");
ofstream g("adn.out");
vector<string> v;
citeste_strings(f, v);
vector<vector<int> > graf;
make_graf(v, graf);
vector<int> rez;
hamiltonian_path(graf, 0, rez);
for(int i = 0; i < rez.size()-1; ++i){
const int a = rez[i], b = rez[i+1];
const int lung = graf[a][b];
for(int i = v[b].size() - lung; i < v[b].size(); ++i){
g << v[b][i]; } }
return 0; }