#include <bits/stdc++.h>
using namespace std;
const string nameFiles = "";
void error(vector<string>::iterator) {}
template <typename T, typename... Args>
void error(vector<string>::iterator cur_var, T a, Args... args) {
  cerr << cur_var->substr((*cur_var)[0] == ' ') << " = " << a << endl;
  error(++cur_var, args...);
}
vector<string> split(string const& str, char c) {
  vector<string> res;
  stringstream ss(str);
  string x;
  while (getline(ss, x, c)) res.emplace_back(x);
  return move(res);
}
string to_str(int x) {
  static char buff[64];
  sprintf(buff, "%d", x);
  return std::string(buff);
}
struct Intersection {
  int len;
  int pos;
  size_t size;
  bool operator<(const Intersection& oth) const {
    return len < oth.len || (len == oth.len && size < oth.size);
  }
};
Intersection getIntersection(const string& sec, const string& main) {
  int pos = 0;
  for (; pos < (int)main.size() && main[pos] != sec[0]; ++pos)
    ;
  if (pos < (int)main.size())
    return {(int)min(main.size() - pos, sec.size()), pos, sec.size()};
  int r_pos = main.size() - 1;
  for (; r_pos >= 0 && main[r_pos] != sec.back(); --r_pos)
    ;
  if (r_pos >= 0) return {r_pos + 1, r_pos, sec.size()};
  return {0, -1, sec.size()};
}
void unionStrings(const string& sec, string& main) {
  Intersection inter = getIntersection(sec, main);
  if (inter.len == 0)
    main += sec;
  else if (main[inter.pos] == sec[0])
    main += sec.substr(inter.len);
  else
    main = sec.substr(0, sec.size() - inter.len) + main;
}
void solve(istream& in, ostream& out) {
  int n;
  in >> n;
  vector<string> s(n);
  for (int i = 0; i < n; ++i) in >> s[i];
  sort(s.begin(), s.end(), [](const string& s1, const string& s2) {
    return s1.size() > s2.size();
  });
  int j, k;
  for (int i = 0; i < n - 1; ++i) {
    bool f = false;
    for (j = i; j < n - 1 && !f; ++j)
      for (k = j + 1; k < n; ++k)
        if (s[j].size() >= s[k].size()) {
          if (getIntersection(s[k], s[j]).len > 0) {
            f = true;
            break;
          }
        } else if (getIntersection(s[j], s[k]).len > 0) {
          swap(s[j], s[k]);
          f = true;
          break;
        }
    j--;
    k = min(k, n - 1);
    swap(s[i + 1], s[j]);
    if (k != i + 1) swap(s[i], s[k]);
    unionStrings(s[i], s[i + 1]);
  }
  out << s.back() << endl;
}
void test(int cnt_tests = 100000) {
  const int kMaxM = 100;
  const int kMaxLen = 26;
  vector<char> letters(kMaxLen);
  iota(letters.begin(), letters.end(), 'a');
  vector<bool> used(kMaxLen, false);
  vector<string> vs;
  for (int i = 0; i < cnt_tests; ++i) {
    vs.clear();
    used.assign(kMaxLen, false);
    int n = 1 + rand() % kMaxLen;
    int m = 1 + rand() % kMaxM;
    cerr << "#" << i << " " << n << endl;
    random_shuffle(letters.begin(), letters.end());
    stringstream in;
    in << m << "\n";
    for (int j = 0; j < m; ++j) {
      int l = rand() % n;
      int r = l + 1 + rand() % (n - l);
      for (int k = l; k < r; ++k) used[k] = true;
      vs.push_back(string(letters.begin() + l, letters.begin() + r));
      in << vs.back() << "\n";
    }
    stringstream out;
    solve(in, out);
    string res = out.str();
    res.pop_back();
    unordered_set<char> us(res.begin(), res.end());
    int len = 0;
    for (int j = 0; j < n; ++j) len += used[j];
    if (res.size() != us.size() || (int)us.size() != len) {
      cerr << "ph1: len = " << len << endl;
      cerr << "res = " << out.str() << endl;
      cerr << in.str() << endl;
      exit(1);
    }
    for (string& s : vs) {
      if (res.find(s) == string::npos) {
        cerr << "res = " << out.str() << endl;
        cerr << in.str() << endl;
        exit(1);
      }
    }
  }
}
int main() {
  srand(time(NULL));
  ios_base::sync_with_stdio(false);
  if (!nameFiles.empty()) {
    freopen((nameFiles + ".in").c_str(), "r", stdin);
    freopen((nameFiles + ".out").c_str(), "w", stdout);
  }
  if (1) {
    solve(cin, cout);
  } else {
    test();
  }
  return 0;
}
