/*
poj 2337 之 有向图 欧拉路径输出
每个单词看作一条有向边,顶点即为单词首尾字母,然后求欧拉路径即可。
1)为保证字典序,先对单词按字典序排序
2)深搜,输出单词序列
*/
1 #include <iostream>
2 #include <fstream>
3 #include <sstream>
4 #include <cstdlib>
5 #include <cstdio>
6 #include <cstddef>
7 #include <iterator>
8 #include <algorithm>
9 #include <string>
10 #include <locale>
11 #include <cmath>
12 #include <vector>
13 #include <cstring>
14 #include <map>
15 #include <utility>
16 #include <queue>
17 #include <stack>
18 #include <set>
19 #include <functional>
20 using namespace std;
21 typedef pair<int, int> PII;
22 typedef long long int64;
23 const int INF = 0x3f3f3f3f;
24 const int modPrime = 3046721;
25 const double eps = 1e-9;
26 const int MaxN = 1010;
27 const int MaxM = 30;
28 const char Opt[4] = { '+', '-', '*', '/' };
29
30 int n;
31
32 struct Edge
33 {
34 int to;
35 int nextEdge;
36 int index;
37 bool isVisited;
38 };
39 Edge edge[MaxN];
40 int head[MaxM];
41
42 int startPot = INF;
43 int in[MaxM];
44 int out[MaxM];
45 vector<string> wordVec;
46 vector<int> eulerPath;
47
48 int total = 0;
49
50
51 //Union-Find Sets
52 int ftr[MaxM];
53 int rnk[MaxM];
54 void ufsIni()
55 {
56 for (int i = 0; i < MaxM; ++i)
57 {
58 ftr[i] = i;
59 rnk[i] = 0;
60 }
61 }
62 int ufsFind(int x)
63 {
64 if (x == ftr[x]) return x;
65 return ftr[x] = ufsFind(ftr[x]);
66 }
67 void ufsUnite(int x, int y)
68 {
69 x = ufsFind(x);
70 y = ufsFind(y);
71 if (x == y) return;
72
73 if (rnk[x] > rnk[y])
74 {
75 ftr[y] = x;
76 }
77 else
78 {
79 ftr[x] = y;
80 if (rnk[x] == rnk[y])
81 {
82 ++rnk[y];
83 }
84 }
85 }
86
87 void addEdge(int u, int v, int index)
88 {
89 edge[total].to = v;
90 edge[total].index = index;
91 edge[total].nextEdge = head[u];
92 edge[total].isVisited = false;
93 head[u] = total++;
94 }
95
96
97 bool isExistEulerpath(int n)
98 {
99
100 ufsIni();
101 for (int i = 0; i < n; ++i)
102 {
103 string word;
104 cin >> word;
105 wordVec.push_back(word);
106 }
107 sort(wordVec.begin(), wordVec.end(), greater<string>());
108 set<int> potSet;
109 for (int i = 0; i < n; ++i)
110 {
111 string word = wordVec[i];
112 int fromCh = word[0] - 'a', toCh = word[word.size() - 1] - 'a';
113 ++out[fromCh];
114 ++in[toCh];
115 addEdge(fromCh, toCh, i);
116 potSet.insert(fromCh);
117 potSet.insert(toCh);
118 startPot = min(startPot, min(fromCh, toCh));
119 ufsUnite(fromCh, toCh);
120 }
121 for (set<int>::iterator it = potSet.begin(); it != potSet.end(); ++it)
122 {
123 if (ufsFind(ftr[*it]) != ufsFind(ftr[*(potSet.begin())]))
124 {
125 return false;
126 }
127 }
128 int tmpU = 0, tmpV = 0;
129 for (set<int>::iterator it = potSet.begin(); it != potSet.end(); ++it)
130 {
131 if (out[*it] - in[*it] == 1)
132 {
133 ++tmpU;
134 if (tmpU > 1)
135 {
136 return false;
137 }
138 startPot = *it;
139 }
140 else
141 {
142 if (out[*it] - in[*it] == -1)
143 {
144 ++tmpV;
145 if (tmpV > 1)
146 {
147 return false;
148 }
149 }
150 else
151 {
152 if (out[*it] - in[*it] != 0)
153 {
154 return false;
155 }
156 }
157 }
158 }
159 if (!((0 == tmpU && 0 == tmpV) || (1 == tmpU && 1 == tmpV)))
160 {
161 return false;
162 }
163 return true;
164 }
165
166
167 void Solve(int pos)
168 {
169 for (int i = head[pos]; i != -1; i = edge[i].nextEdge)
170 {
171 if (!edge[i].isVisited)
172 {
173 edge[i].isVisited = true;
174 Solve(edge[i].to);
175 eulerPath.push_back(edge[i].index);
176 }
177 }
178 }
179
180 int main()
181 {
182 #ifdef HOME
183 freopen("in", "r", stdin);
184 //freopen("out", "w", stdout);
185 #endif
186
187 int T;
188 cin >> T;
189 while (T--)
190 {
191 startPot = INF;
192 total = 0;
193 fill(head, head + MaxM, -1);
194 fill(in, in + MaxM, 0);
195 fill(out, out + MaxM, 0);
196 cin >> n;
197 if (isExistEulerpath(n))
198 {
199 Solve(startPot);
200 for (vector<int>::reverse_iterator rIt = eulerPath.rbegin(); rIt != eulerPath.rend(); ++rIt)
201 {
202 cout << wordVec[*rIt];
203 if (rIt != eulerPath.rend() - 1)
204 {
205 cout << ".";
206 }
207 else
208 {
209 cout << endl;
210 }
211 }
212 }
213 else
214 {
215 cout << "***" << endl;
216 }
217 wordVec.clear();
218 eulerPath.clear();
219 }
220
221
222 #ifdef HOME
223 cerr << "Time elapsed: " << clock() / CLOCKS_PER_SEC << " ms" << endl;
224 _CrtDumpMemoryLeaks();
225 #endif
226 return 0;
227 }