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.
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.
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.
2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
aloha.arachnid.dog.gopher.rat.tiger
***
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; }