Catenyms+欧拉回路/欧拉路+并查集+POJ

Catenyms
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 9617 Accepted: 2524

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
***
解决方案:首先建立一个图的模型:用单词的首尾字母做节点,用单词做边。这些单词能连成一串,必须:1)能形成欧拉路或欧拉回路;2)要能构成一个连通图。先是判断是欧拉回路/欧拉路:记录每个顶点的出度与入度,若有一个点出度大入度1,一个点入度大出度1,其余出度等于入度,则为欧拉路;若每个店的出度与入度都相等,则为欧拉回路。然后判断是否为联通的,可用并查集来判断,这个就不用说了。然后是怎么建图的问题,这个最好用头插法建图,记录每个顶点所连得边及终点。再则就是输出要字典序的问题,可以这样子,先对单词进行排序,然后按照排好顺序的单词建图,是要从小到大,还是从大到小排呢。若是从小到大,由于是头插法建图,那么每次遍历边的时候,这个按字典序从大到小来遍历的,所以,我们应该从大到小排序。还有就是起点的问题:若是欧拉路,则出度比入度大1的那个点做起点,若是欧拉回路,字典序最小的点做起点。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define MMAX 1003
using namespace std;
string word[MMAX];
int fa[27],in[27],out[27];
int Map[27][27];
bool vise[MMAX];
bool cmp(string a,string b)
{
    return a>b;
}
stack<string>S;
int N,k;
int head[27];
struct node
{
    int from,to;
    string word;
    int next;
} E[MMAX];
void add(int from,int to,string word)
{
    E[k].from=from;
    E[k].to=to;
    E[k].word=word;
    E[k].next=head[from];
    head[from]=k++;
}
bool vis[27];
int faset(int x)
{

    return x!=fa[x]?fa[x]=faset(fa[x]):x;
}
int C(char c)
{
    return int(c-'a');
}
void dfs(int v)
{
    for(int i=head[v]; i!=-1; i=E[i].next)
    {
        if(!vise[i])
        {
            vise[i]=true;
            dfs(E[i].to);
            S.push(E[i].word);

        }
    }

}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(head,-1,sizeof(head));
        memset(vis,false,sizeof(vis));
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        memset(Map,0,sizeof(Map));
        memset(vise,false,sizeof(vise));
        while(!S.empty())S.pop();
        for(int i=0; i<26; i++)
        {
            fa[i]=i;
        }
        scanf("%d",&N);
        for(int i=0; i<N; i++)
        {
            cin>>word[i];
        }
        sort(word,word+N,cmp);
        k=0;
        for(int i=0; i<N; i++)
        {
            int len=word[i].length();
            int st=C(word[i][0]),en=C(word[i][len-1]);
            add(st,en,word[i]);
            int x=faset(st),y=faset(en);
            vis[st]=true,vis[en]=true;
            if(x!=y)
            {
                fa[x]=y;
            }
            in[en]++,out[st]++;
        }
        bool flag=true;
        int pre=faset(C(word[0][0]));
        for(int i=0; i<26; i++)
        {
            if(vis[i])
            {
                if(pre!=faset(i))
                {
                    flag=false;
                    break;
                }
            }
        }
        int st,cst=0,cen=0,cnt=0;
        st=C(word[N-1][0]);
        for(int i=0; i<26; i++)
        {
            if(in[i]!=out[i])
            {
                if(abs(in[i]-out[i])>1)
                {
                    flag=false;
                    break;
                }
                if(in[i]+1==out[i]) cst++,st=i;

                if(out[i]+1==in[i]) cen++;
                cnt++;
            }
        }
        if((cst==1&&cen==1)||!cnt) ;
        else flag=false;
        if(flag)
        {
            dfs(st);
            while(!S.empty())
            {
                string temp=S.top();
                S.pop();
                cout<<temp<<".";
                if(S.size()==1) break;
            }
            cout<<S.top()<<endl;
            S.pop();
        }
        else
        {
            printf("***\n");
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值