Catenyms
Time Limit: 1000 ms Memory Limit: 65536 kB Solved: 148 Tried: 1114
Description
A catenym is a pair of words separated by a period such that the last letter of the first word is the same as the first letter of the second. For example, the following are catenyms:
dog.gopher
gopher.rat
rat.tiger
aloha.aloha
arachnid.dog
A compound catenym is a sequence of three or more words separated by periods such that each adjacent pair of words forms a catenym. For example,
aloha.aloha.arachnid.dog.gopher.rat.tiger
Given a dictionary of lower case words, you are to find a compound catenym that contains each of the words exactly once.
Input
The first line of standard input contains t, the number of test cases. Each test case begins with 3 <= n <= 1000 - the number of words in the dictionary. n distinct dictionary words follow; each word is a string of between 1 and 20 lowercase letters on a line by itself.
Output
For each test case, output a line giving the lexicographically least compound catenym that contains each dictionary word exactly once. Output "***" if there is no solution.
Sample Input
2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
Sample Output
aloha.arachnid.dog.gopher.rat.tiger
***
Source
Waterloo local 2003.01.25
/*算法思想:
给几个单词,要求用完所有的单词一遍进行单词接龙,并且使接得的字符串的字典序最小
我们可以把每个单词抽象成一条边,边的起点是单词的第一个字符,边的终点是单词的最后一个字符
边的权值就是这个单词,这样,我们就构造出了一个图,问题就转化成了求一条这个图的欧拉路径,使
得欧拉路径上的单词拼接起来的字符串的字典序最小。
首先,我们要判断这个图是不是可以存在一个欧拉路径,使得我们能够不重复的访问完所有的边。
判断的方法就是统计图中所有节点的出度入度,要是出度入度不等,不存在,要是所有的点的出度均等于
入度,那么就找一个字典序最小的点作为起始点。
然后,我们需要判断图是不是只有一个联通块,只有在一个联通块的前提下,我们才能正确的找出欧拉路径
最后就是找欧拉路径了,需要对单词进行排序,排序的规则是按照单词的字典序从大到小排序,因为最终
我们输出答案的时候是逆着输出的,这样才能保证输出的路径满足字典序最小的规则。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2000
using namespace std;
struct data1
{
char word[30];
} s[N],ans[N];
struct data2
{
int st,en;
char val[30];
int next;
} edge[N];
int n,tot,st_id;
int in[30],out[30],head[30],fa[30];
bool vs[N],use[30];
void make_set()
{
for(int i=0;i<30;i++)
fa[i]=i;
}
int find_set(int x)
{
if(fa[x]!=x) fa[x]=find_set(fa[x]);
return fa[x];
}
void merge_set(int x,int y)
{
fa[y]=x;
}
void add_edge(int st,int en,char val[]) //加边
{
in[en]++;
out[st]++;
use[st]=use[en]=true;
edge[tot].st=st;
edge[tot].en=en;
strcpy(edge[tot].val,val);
edge[tot].next=head[st];
head[st]=tot++;
int f1=find_set(st);
int f2=find_set(en);
if(f1!=f2) merge_set(f1,f2);
}
bool cmp(data1 a,data1 b) //比较函数
{
if(strcmp(a.word,b.word)>0) return true;
else return false;
}
bool judge() //判断图中是否存在一条欧拉路径
{
st_id=-1;
int sum1=0,sum2=0,set_num=0;
for(int i=0;i<26;i++)
if(use[i])
{
if(find_set(i)==i) set_num++;
if(set_num>1) return false;
if(in[i]-out[i]==1) sum2++;
else if(in[i]-out[i]==-1)
{
sum1++;
st_id=i;
}
else if(in[i]!=out[i]) return false;
}
if(set_num!=1) return false; //如果不止一个联通块,不存在
if(!((sum1==1 && sum2==1) || (sum1==0 && sum2==0))) return false; //通过点的入度出度判断是否存在
if(st_id==-1) //如果是欧拉回路,我们要找一个最小的点作为起始点
{
for(int i=0;i<26;i++)
if(use[i] && out[i]>0)
{
st_id=i;
return true;
}
}
if(sum1==1 && sum2==1) return true;
return false;
}
void eular(int v,int id) //dfs找欧拉回路
{
for(int pos=head[v];pos!=-1;pos=edge[pos].next)
if(!vs[pos])
{
vs[pos]=true;
eular(edge[pos].en,pos);
}
if(id!=-1) strcpy(ans[++tot].word,edge[id].val);
}
int main()
{
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(head,-1,sizeof(head));
memset(edge,0,sizeof(edge));
memset(use,0,sizeof(use));
tot=1;
make_set();
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%s",s[i].word);
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++)
{
int st=s[i].word[0]-'a';
int en=s[i].word[strlen(s[i].word)-1]-'a';
add_edge(st,en,s[i].word);
}
if(judge())
{
tot=0;
memset(ans,0,sizeof(ans));
memset(vs,0,sizeof(vs));
eular(st_id,-1);
for(int i=tot;i>=1;i--)
{
printf("%s",ans[i].word);
if(i!=1) printf("."); else printf("\n");
}
}
else printf("***\n");
}
return 0;
}