Catenyms
Time Limit: 1000MS | | Memory Limit: 65536K |
Total Submissions: 13493 | | Accepted: 3517 |
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 last 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
算法分析
题意:(来自饶齐大佬)
给你一组N个单词,现在要你输出这样一组单词序列。该序列包含了所有N个单词,且该序列中的前一个单词的最后一个字母与后一个单词的第一个字母相同。如果存在多个这种首尾相连的序列,就输出字典序最小的那个即可。
分析:
首先可以看出,这题应该是有向图判断欧拉路径:
欧拉路径存在的充要条件:
1. 有向图存在欧拉回路的充要条件
所有顶点的 入度 和 出度 的和是 偶数,且该图是连通图(并查集)。
2.有向图含有欧拉通路的充要条件
起始点s 的入度=出度-1,结束点t的出度=入度-1 或两个点的入度=出度,且该图是连通图(并查集)。
此题关键是字典序最小:
首先对其字典序排序,此题,我用的是链式向前星做的,对于一个点插入的话,后插的保存到最前面,即遍历是在最前面,所以这个排序如果首字母相同就按照字典序大的排,其他正常字典序排就行。
代码实现:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<math.h>
#include<set>
#include<vector>
#include<sstream>
#include<queue>
#define ll long long
#define PI 3.1415926535897932384626
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=2000;
struct Edge
{
int to,next,id;
bool flag;
}edge[maxn];
int head[maxn];
int tot;
void addedge(int u,int v,int w)
{
edge[tot].to=v;
edge[tot].id=w;
edge[tot].next=head[u];
edge[tot].flag=false;
head[u]=tot++;
}
int start,n;
int f[30],in[30],out[30];
vector<int>ans;
void init()
{
tot=0;
ans.clear();
for (int i = 1; i <= 26; i++)
f[i] = i;
memset(head,-1,sizeof(head));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
}
int find(int x)
{
return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x, int y)
{
int t1, t2;
t1 = find(x); t2 = find(y);
if (t1 != t2) f[t2] = t1;
else return;
}
///判断是否存在欧拉通路径
///>=0:欧拉通路的起始位置
///-2:欧拉回路
///-1:无欧拉路劲
int isEluer() //判断是否存在欧拉通路径
{
int sum=0;
for(int i=0;i<26;i++) //保证是一个连通图
{
if((in[i]||out[i])&&find(i)==i)
sum++;
if(sum>=2)
return -1;
}
int ac=0,bc=0,pos=-1;
for(int i=0;i<26;i++)
{
if(in[i]==out[i]) continue;
if(in[i]==out[i]+1)
{
ac++;
continue;
}
if(in[i]==out[i]-1)
{
pos=i;
bc++;
continue;
}
return -1;
}
if(!ac&&!bc) return -2;
if(ac==1&&bc==1) return pos;
return -1;
}
void dfs(int x)
{
for(int i=head[x];i!=-1;i=edge[i].next)
{
if(!edge[i].flag)
{
edge[i].flag=true;
dfs(edge[i].to);
ans.push_back(i);//记录边的号码
}
}
}
string word[maxn];
void print()
{
for(int i=ans.size()-1;i>0;i--)
cout <<word[ans[i]]<< '.';
cout <<word[ans[0]] << endl;
}
bool cmp(const string a, const string b)
{
if(a[0]==b[0])
return a>b;
return a<b;
}
int main()
{
int t;
cin>>t;
while(t--)
{
init();
scanf("%d",&n);
for(int i=0;i<n;i++)
{
cin>>word[i];
}
sort(word,word+n,cmp);
for(int i=0;i<n;i++)
{
int x,y;
x=word[i][0]-'a';
y=word[i][word[i].size()-1]-'a';
addedge(x,y,i);
merge(x,y);
in[y]++;
out[x]++;
}
int flag=isEluer();
if(flag==-1)
printf("***\n");
else if(flag==-2)
{
dfs(word[0][0]-'a');
print();
}
else
{
dfs(flag);
print();
}
}
return 0;
}