这题还是比较难的。
首先建图方面,如果单纯的把单词作为点,能拼接的关系作为边,那么就是哈密顿图(每个点仅能走一次),难度比较大。
换一种思路,就是把每个单词看成一条有向边,由该单词的首字母指向尾字母。
那么这题便是欧拉图的问题了。
本质上采用的还是搜索,但是为了快速得到字典序最小的欧拉路径,首先要对单词集进行排序。
排完序后,用边集数组存图;再通过计算各点的出度与入度,同时判断基图(不考虑边的方向的图)的连通性,判断是否存在欧拉回路或欧拉通路。
如果存在欧拉回路,那么就从0开始搜索;
如果存在欧拉通路,那么就从“出度=入度+1”的点开始搜索。
参考代码如下:
边集数组存图+并查集判断连通性
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
struct Enode{
int u;
int v;
string s;
};
Enode edge[1010];
string s[1010];
int n,m,fa[1010];
int flag[1010];
int path[1010];
int visit[1010];
int root(int x)
{
if (fa[x]==x) return x;
return fa[x]=root(fa[x]);
}
void uset(int x,int y)
{
int a=root(x);
int b=root(y);
if (a!=b)
fa[b]=a;
}
//并查集判断连通性
int judge()
{
int t=0;
for (int i=0;i<26;++i)
if (flag[i] && fa[i]==i) t++;
return t == 1;
}
void find(int u)
{
for (int i=0;i<n;++i)
if (!visit[i] && edge[i].u==u)
{
visit[i]=1;
find(edge[i].v);
path[m++]=i;
}
}
int main()
{
int T;
cin >> T;
while (T--)
{
memset(visit,0,sizeof(visit));
memset(flag,0,sizeof(flag));
// memset(edge,0,sizeof(edge));
memset(path,0,sizeof(path));
memset(fa,0,sizeof(fa));
cin >> n;
int i;
for (i=0;i<n;++i)
{
cin >> s[i];
}
sort(s,s+n);
for (i=0;i<26;++i) fa[i]=i;
int in[30],out[30];
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for (i=0;i<n;++i)
{
int u=s[i][0]-'a';
int v=s[i][s[i].length()-1]-'a';
edge[i].u=u;
edge[i].v=v;
edge[i].s=s[i];
in[v]++;
out[u]++;
flag[u]=flag[v]=1;
uset(u,v);
// cout << edge[i].s << endl;
}
int start=edge[0].u;
int cnt1=0,cnt2=0;
//这段代码感觉很经典。。
for (i=0;i<26;++i)
{
if (in[i]==out[i]) continue;
else
if (in[i]==out[i]+1)
{
cnt1++;
}
else if (in[i]==out[i]-1)
{
cnt2++;
start=i;
}
else break;
}
if (i==26 && (cnt1==cnt2||(cnt1<2)) && judge())
{
m=0;
find(start);
for (i=m-1;i>0;--i)
cout << edge[path[i]].s << ".";
cout << edge[path[0]].s << endl;
}
else cout << "***" << endl;
}
return 0;
}