1 /*
2 1.先用并差集判断图是否连通
3 2.判断是否存在欧拉路或欧拉回路
4 3.DFS求欧拉路径
5 */
6 #include<iostream>
7 #include<cstdio>
8 #include<cstring>
9 #include<algorithm>
10 using namespace std;
11
12 const int size = 1005;
13
14 struct nodedge
15 {
16 int e;//单词的最后一个字母 对应的数字 相当于:路径的末端
17 int id;//记录这个单词是第几个单词
18 int next;//首字母相同的单词连起来
19 bool vis;//标志这个单词是否用过
20 }edge[size];
21
22 struct nodechar
23 {
24 char ch[32];//存放单词
25 }cha[size];
26
27 int degree[27];//计算出现的 字母的 初度入度
28 int father[27];//并差集
29 int rank[27];
30 bool show[27];
31 int stack[size]; //存放欧拉路径
32 int k[27];// k[i] 存放以 以 i 为首的字母链的所在位置
33 int st;//记录路径 stack[]
34 int point;//记录出现了几个字母
35 int M;
36
37 int find(int x)//非递归压缩路径
38 {
39 int i=x;
40 while(i!=father[i])
41 i=father[i];
42 int j=x;
43 while(j!=i)
44 {
45 int k= father[j];
46 father[j]=i;
47 j=k;
48 }
49 return i;
50 }
51
52 void uion(int x,int y)//并差集 合并两个子集
53 {
54 x=find(x);
55 y=find(y);
56 if(x!=y)
57 {
58 if(rank[x]>=rank[y])
59 {
60 rank[x]+=rank[y];
61 father[y]=x;
62 }
63 else
64 {
65 rank[y]+=rank[x];
66 father[x]=y;
67 }
68 }
69 }
70
71 bool cmp(nodechar x,nodechar y)
72 {
73 return strcmp(x.ch,y.ch)>0;
74 }
75
76 void init()
77 {
78 int i,len;
79 for(i=0;i<26;++i)
80 {
81 degree[i]=0;
82 father[i]=i;
83 rank[i]=1;
84 show[i]=false;
85 k[i]=-1;
86 }
87 st=0;
88 point=0;
89
90 for(i=0;i<M;++i)
91 scanf("%s",cha[i].ch);
92
93 sort(cha,cha+M,cmp);//从 大 到 小 排序 (插入到之后得到的是从小大排序)
94 for(i=0;i<M;++i)
95 printf("%s\n",cha[i].ch);
96
97 for(i=0;i<M;++i)
98 {
99 len=strlen(cha[i].ch);
100 int h1 = cha[i].ch[0]-'a';
101 int h2 = cha[i].ch[len-1]-'a';
102
103 edge[i].e=h2;//依次插入之后 变成从 小到大排序
104 edge[i].id=i;
105 edge[i].vis=false;
106 edge[i].next=k[h1];
107 k[h1]=i;
108 degree[h1]--;//注意这里设计的
109 degree[h2]++;
110 if(!show[h1]){show[h1]=true;point++;}
111 if(!show[h2]){show[h2]=true;point++;}
112 uion(h1,h2);
113 }
114 for(i=0;i<26;++i)
115 if(show[i])find(i);
116 }
117
118 int start()
119 {
120 int odd=0,t1,t2;//t1,t2记录两个奇数度结点的位置
121 int i;
122
123 for(i=0;i<26;++i)
124 if(show[i])
125 {
126 if(rank[father[i]]!=point)return -1;
127 if(!degree[i])continue;
128 if(degree[i]>1 || degree[i]<-1)return -1;//少了这个条件就错了 (也即可能有 odd==2,奇数的出度,或入度不是 1,或-1)
129 if(0==odd)t1=i;
130 if(1==odd)t2=i;
131 odd++;
132 }
133
134 if(odd==1 || odd>2 || (degree[t1]+degree[t2]))return -1;//第三个条件限制一个入度为 1 ,一个出度为1
135 if(odd==2)//存在欧拉路
136 {
137 if(degree[t1]==-1)return t1;
138 else return t2;
139 }
140 if(odd==0)//存在欧拉回路
141 {
142 for(i=0;i<26;++i)
143 if(show[i])return i;
144 }
145 return -1;
146 }
147
148 void dfs(int t)
149 {
150 int i;
151 for(i=k[t];i!=-1;i=edge[i].next)
152 {
153 if(edge[i].vis==0)
154 {
155 edge[i].vis=1;
156 dfs(edge[i].e);
157 stack[st++]=edge[i].id;//单词ID是以 单词的末尾字母存的 所以提取末尾获得单词ID
158 }//同一个起点可能引出多条边 从这个点用一个单词(一条边)得到一个新的起点此时从新起点开始
159 }// 以上从一个起点选边都是 选这个起点 最小的边(注意以上排序)
160 }
161
162 int main()
163 {
164 int i,iscase;
165 scanf("%d",&iscase);
166 while(iscase--)
167 {
168 scanf("%d",&M);
169 init();
170 int t=start();
171 if(-1==t)printf("***\n");
172 else
173 {
174 dfs(t);
175 printf("%s",cha[stack[st-1]].ch);
176 for(i=st-2; i>=0;--i)
177 printf(".%s",cha[stack[i]].ch);
178 printf("\n");
179 }
180 }
181 return 0;
182 }