Catenyms
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 |
思路:
建图+找欧拉路径。
感想:
这题难在保证答案字典序最小,注意‘.’的字典序最小,所以一个点到一个点有多条路径时要首先访问边(即单词)字典序小的(我是用set来处理的),然后点的访问顺序也要按字母顺序来(建边时处理一下就够了),这样就能保证整体字典序最小了。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <set>
#define maxn 30
using namespace std;
int n,m,ans,cnt,num,xxc,sx,xxs;
bool vis[1005],app[maxn];
int pre[maxn];
int in[maxn],out[maxn];
int sta[1005];
char s[1005][30],ts[30];
set<string>ss[maxn][maxn];
string s1;
int pp[maxn];
struct Node
{
int v,next;
} edge[1005];
void init()
{
int i;
for(i=0; i<=26; i++)
{
pre[i]=i;
}
num=0;
}
int sfind(int x) // 查找时优化了路径
{
while(pre[x]!=x) x=pre[x];
return x;
}
void smerge(int a,int b) // 合并时将深度小的集合合并到大的里面
{
int x,y;
x=sfind(a);
y=sfind(b);
if(x!=y)
{
pre[x]=y;
num++;
}
}
void init1()
{
int i,j;
memset(pp,0,sizeof(pp));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(app,0,sizeof(app));
memset(vis,0,sizeof(vis));
for(i=0;i<26;i++)
{
for(j=0;j<26;j++)
{
ss[i][j].clear();
}
}
}
void addedge(int u,int v)
{
cnt++;
edge[cnt].v=v;
edge[cnt].next=pp[u];
pp[u]=cnt;
}
bool isok()
{
int i,j,t,u,v;
if(num!=xxc-1) return false ;
u=v=0;
for(i=0; i<26; i++)
{
if(in[i]==out[i]) continue ;
t=in[i]-out[i];
if(t==1) u++;
else if(t==-1) v++,sx=i;
else return false ;
}
if(u==1&&v==1) return true ;
else if(u==0&&v==0)
{
for(i=0;i<26;i++)
{
if(app[i])
{
sx=i;
break ;
}
}
return true ;
}
return false ;
}
void dfs(int u)
{
int i,j;
for(i=pp[u];i;i=edge[i].next)
{
if(!vis[i])
{
vis[i]=1;
dfs(edge[i].v);
}
}
sta[++xxs]=u;
}
int main()
{
int i,j,t,len,u,v,k;
scanf("%d",&t);
while(t--)
{
init();
init1();
scanf("%d",&n);
cnt=xxc=0;
for(i=1; i<=n; i++)
{
scanf("%s",s[i]);
}
for(i=1;i<=n;i++) // 用STL用不好 只能自己写冒泡排序了
{
strcpy(ts,s[i]);
k=i;
for(j=i+1;j<=n;j++)
{
if(strcmp(ts,s[j])<0)
{
k=j;
strcpy(ts,s[j]);
}
}
strcpy(ts,s[i]);
strcpy(s[i],s[k]);
strcpy(s[k],ts);
}
for(i=1;i<=n;i++) // 从大到小排序后倒着建边
{
len=strlen(s[i]);
u=s[i][0]-'a';
v=s[i][len-1]-'a';
smerge(u,v);
ss[u][v].insert(s[i]);
if(!app[u]) app[u]=1,xxc++;
if(!app[v]) app[v]=1,xxc++;
out[u]++;
in[v]++;
addedge(u,v);
}
if(isok()) // 判断能否形成欧拉路径
{
xxs=0;
dfs(sx); // dfs找欧拉路径
for(i=xxs;i>1;i--)
{
u=sta[i];
v=sta[i-1];
s1=*(ss[u][v].begin()); // 用到了set来保证字典序最小
if(i==xxs) cout<<s1;
else cout<<"."<<s1;
ss[u][v].erase(s1);
}
printf("\n");
}
else printf("***\n");
}
return 0;
}
/*
2
5
aa
ab
aba
ba
bb
3
ab
ba
abca
ans:
aa.ab.bb.ba.aba
ab.ba.abca
*/