#include <iostream>
#include <vector>
#include <algorithm>
#include <array>
using namespace std;
typedef array<int, 52> rexico;

rexico operator -(const rexico &a){
    auto res = a;
    for(auto &e: res){
        e = -e;
    }
    return res;
}
rexico& operator +=(rexico &a, const rexico &b){
    for(int i=0; i<52; i++){
        a[i] += b[i];
    }
    return a;
}
rexico operator +(const rexico &a, const rexico &b){
    auto res = a;
    return res += b;
}
rexico operator *(const int &c, const rexico &a){
    auto res = a;
    for(auto &e: res){
        e *= c;
    }
    return res;
}


template<typename T>
struct MincostFlow{
    struct edge{
        int to, rev;
        int cap;
        T cost;
        edge(int to, int rev, int cap, T cost)
            :to(to),rev(rev),cap(cap),cost(cost){}
        edge(){}
    };
    
    int n;
    T zero;
    T inf;
    vector<vector<edge>> graph;

    MincostFlow(int n, T zero, T inf):n(n),zero(zero),inf(inf){
        graph.resize(n);
    }
    void add_edge(int from, int to, int cap, T cost){
        graph[from].emplace_back(to, graph[to].size(), cap, cost);
        graph[to].emplace_back(from, (int)graph[from].size()-1, 0, -cost);
    }
    T exec(int s, int g, int f){
        T res = zero;
        while(f > 0){
            vector<int> prevv(n), preve(n);
            vector<T> mincost(n, inf);
            mincost[s] = zero;
            while(1){
                bool update = false;
                for(int i=0; i<n; i++){
                    if(mincost[i] == inf) continue;
                    for(int j=0; j<(int)graph[i].size(); j++){
                        edge &e = graph[i][j];
                        if(e.cap>0 && mincost[i] +e.cost < mincost[e.to]){
                            mincost[e.to] = mincost[i] +e.cost;
                            prevv[e.to] = i;
                            preve[e.to] = j;
                            update = true;
                        }
                    }
                }
                if(!update) break;
            }
            if(mincost[g] == inf){
                return inf;
            }
         
            int d = f;
            for(int v=g; v!=s; v=prevv[v]){
                d = min(d, graph[prevv[v]][preve[v]].cap);
            }
            f -= d;
            res += d*mincost[g];
            for(int v=g; v!=s; v=prevv[v]){
                edge &e = graph[prevv[v]][preve[v]];
                e.cap -= d;
                graph[v][e.rev].cap += d;
            }
        }
        return res;
    }
};

char int2char(int n){
    if(n<26) return 'A'+n;
    return 'a'+n-26;
}
int char2int(char c){
    if('A'<=c and c<='Z'){
        return c-'A';
    }
    return c-'a'+26;
}
rexico conv(char c){
    rexico res{};
    res[char2int(c)] = -1;
    return res;
}

int main(){
    rexico zero{}, inf;
    for(int i=0; i<52; i++){
        inf[i] = 1e9;
    }
    int n;
    cin >> n;
    vector<string> s(n);
    for(int i=0; i<n; i++){
        cin >> s[i];
    }
    MincostFlow<rexico> mcf(2*n+2, zero, inf);
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            mcf.add_edge(i, n+j, 1, conv(s[i][j]));
        }
        mcf.add_edge(2*n, i, 1, zero);
        mcf.add_edge(n+i, 2*n+1, 1, zero);
    }
    rexico ret = mcf.exec(2*n, 2*n+1, n);
    string ans = "";
    for(int i=0; i<52; i++){
        ans += string(-ret[i], int2char(i));
    }
    sort(ans.begin(), ans.end());
    cout << ans << endl;
    return 0;
}



Measured memory_bytes: 4456