// implement 31 min #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; typedef long long ll; typedef unsigned int uint; typedef unsigned long long ull; static const long double EPS = 1e-9; //static const long double INF = 1e+8; static const long double PI = acos(-1.0); #define REP(i, n) for (int i = 0; i < (int)(n); i++) #define FOR(i, s, n) for (int i = (s); i < (int)(n); i++) #define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++) #define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++) #define MEMSET(v, h) memset((v), h, sizeof(v)) const vector ZERO(52, 0); const vector INF(52, 1000); namespace std { vector operator+(const vector &lhs, const vector &rhs) { assert(lhs.size() == rhs.size()); vector ret = lhs; REP(i, ret.size()) { ret[i] += rhs[i]; } return ret; } vector operator-(const vector &lhs, const vector &rhs) { assert(lhs.size() == rhs.size()); vector ret = lhs; REP(i, ret.size()) { ret[i] -= rhs[i]; } return ret; } vector &operator+=(vector &lhs, const vector &rhs) { assert(lhs.size() == rhs.size()); REP(i, lhs.size()) { lhs[i] += rhs[i]; } return lhs; } vector &operator-=(vector &lhs, const vector &rhs) { assert(lhs.size() == rhs.size()); REP(i, lhs.size()) { lhs[i] -= rhs[i]; } return lhs; } vector operator-(const vector &lhs) { vector ret(lhs.size()); REP(i, ret.size()) { ret[i] = -lhs[i]; } return ret; } }; typedef vector Weight; struct Edge { int src; int dest; int capacity; Weight cost; Edge(int src, int dest, int capacity, Weight cost) : src(src), dest(dest), capacity(capacity), cost(cost) {;} bool operator<(const Edge &rhs) const { if (cost != rhs.cost) { return cost > rhs.cost; } if (src != rhs.src) { return src < rhs.src; } return dest < rhs.dest; } }; typedef vector Edges; typedef vector Graph; typedef vector Array; typedef vector Matrix; void PrintCost(const Weight &cost) { REP(i, cost.size()) { if (cost[i] == 0) { continue; } char c; if (i < 26) { c = 'z' - i; } else { c = 'Z' - (i - 26); } if (cost[i] < 0) { cout << '-'; } REP(j, abs(cost[i])) { cout << c; } cout << endl; } } pair MinCostFlow(const Graph &g, int s, int t) { const int n = g.size(); vector > capacity(n, vector(n, 0)); Matrix cost(n, Array(n, Weight(52, 0))); for (int from = 0; from < n; from++) { for (Edges::const_iterator it = g[from].begin(); it != g[from].end(); it++) { capacity[it->src][it->dest] += it->capacity; cost[it->src][it->dest] += it->cost; } } pair ret = make_pair(0, ZERO); vector parent(n); vector prev_dist(n, vector(52, 0)); vector now_dist(n); // calc potential for (int iter = 0; iter < n; iter++) { bool end = true; for (int from = 0; from < n; from++) { for (int i = 0; i < (int)g[from].size(); i++) { if (g[from][i].capacity <= 0) { continue; } int to = g[from][i].dest; Weight ncost = prev_dist[from] + g[from][i].cost; if (ncost < prev_dist[to]) { end = false; prev_dist[to] = ncost; } } } if (end) { break; } if (iter == n - 1) { assert(false); // exist negative cycle } } while (true) { fill(parent.begin(), parent.end(), -1); fill(now_dist.begin(), now_dist.end(), INF); priority_queue que; que.push(Edge(s, s, 0, ZERO)); now_dist[s] = ZERO; while (!que.empty()) { Edge node = que.top(); que.pop(); if (parent[node.dest] != -1) { continue; } parent[node.dest] = node.src; int from = node.dest; for (Edges::const_iterator it = g[from].begin(); it != g[from].end(); it++) { int to = it->dest; Weight ncost = node.cost + cost[from][to] + (prev_dist[from] - prev_dist[to]); if (capacity[from][to] == 0) { continue; } if (ncost >= now_dist[to]) { continue; } now_dist[to] = ncost; que.push(Edge(from, to, 0, ncost)); } } if (parent[t] == -1) { break; } int from = parent[t]; int to = t; int flow = 1; ret.first += flow; from = parent[t]; to = t; while (from != to) { ret.second += cost[from][to]; capacity[from][to] -= flow; capacity[to][from] += flow; from = parent[from]; to = parent[to]; } for (int i = 0; i < n; i++) { prev_dist[i] += now_dist[i]; } } return ret; } void AddEdge(Graph &g, int from, int to, int capacity, Weight cost) { g[from].push_back(Edge(from, to, capacity, cost)); g[to].push_back(Edge(to, from, 0, -cost)); } vector GetCost(int c) { vector ret = ZERO; if (isupper(c)) { ret[(c - 'A')] = -1; } if (islower(c)) { ret[(c - 'a') + 26] = -1; } return ret; } int n; char field[100][100]; inline int IN(int y) { return y; } inline int OUT(int x) { return n + x; } inline int SOURCE() { return n * 2; } inline int DEST() { return n * 2 + 1; } inline int SIZE() { return n * 2 + 2; } int main() { while (scanf("%d", &n) > 0 && n) { REP(y, n) { int v = scanf("%s", field[y]); assert(v == 1); } Graph g(SIZE()); REP(y, n) { AddEdge(g, SOURCE(), IN(y), 1, ZERO); AddEdge(g, OUT(y), DEST(), 1, ZERO); REP(x, n) { AddEdge(g, IN(y), OUT(x), 1, GetCost(field[y][x])); } } vector vect = MinCostFlow(g, SOURCE(), DEST()).second; string ans = ""; REP(i, 52) { REP(j, abs(vect[i])) { if (i < 26) { ans += 'A' + i; } else { ans += 'a' + i - 26; } } } cout << ans << endl; } }