Catenyms

【题目描述】

现给定一组单词,如果存在某两个单词,一个单词的前缀和另一个单词的后缀相同,则认为这两个单词可以相连,例如“abce”与“efdg”可以相连,询问这组单词能否排成一排,如果可以,则输出字典序最小的排列方式。

【输入描述】

输入多组数据,对于每组数据:

第一行输入一个正整数N(3 <= N <= 1000),表示此组单词的数目;

接下来N行,每行输入一个单词,每个单词的长度不超过20。

【输出描述】

对于每组输入数据,输出一行,表示排列方式,每两个单词之间用一个“.”隔开,如果不存在合法的排列方式,则输出“***”。

【输入样例】

2

6

aloha

arachnid

dog

gopher

rat

tiger

3

oak

maple

elm

【输出样例】

aloha.arachnid.dog.gopher.rat.tiger

***

源代码:

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct Node
{
    int To,Next,Number;
    bool Flag;
}Edge[2001];
int T,n,Num,Sum,In[30],Out[30],Head[30],Ans[1001];
string S[1001];
void Add(int t1,int t2,int Number) //边表。
{
    Edge[++Num].To=t2;
    Edge[Num].Next=Head[t1];
    Edge[Num].Number=Number; //所属单词编号。
    Edge[Num].Flag=false;
    Head[t1]=Num;
}
void DFS(int T) //深搜。
{
    for (int a=Head[T];a;a=Edge[a].Next) //注意顺序,值得学习,倒序遍历,故字典序最小。
      if (!Edge[a].Flag)
      {
        Edge[a].Flag=true;
        DFS(Edge[a].To);
        Ans[Sum++]=Edge[a].Number; //记录。
      }
}
int main()
{
    cin>>T;
    while (T--)
    {
        cin>>n;
        for (int a=0;a<n;a++)
          cin>>S[a];
        sort(S,S+n); //按照字典序排序。
        Num=Sum=0;
        memset(In,0,sizeof(In));
        memset(Out,0,sizeof(Out));
        memset(Head,0,sizeof(Head));
        int Start=26;
        for (int a=n-1;a>=0;a--) //字典序大的先加入。
        {
            int t1=S[a][0]-'a'; //开头。
            int t2=S[a][S[a].length()-1]-'a'; //结尾。
            Add(t1,t2,a); //在首位之间建立一条有向边。
            Out[t1]++; //出度。
            In[t2]++; //入度。
            Start=min(Start,min(t1,t2)); //字典序最小的字母。
        }
        int T1(0),T2(0);
        for (int a=0;a<26;a++)
          if (Out[a]-In[a]==1) //只能最先出。
          {
            T1++;
            Start=a; //按照欧拉回路,如果有一个出度比入度大1的点,就从这个点出发,否则从字典序最小的点出发。
          }
          else
            if (Out[a]-In[a]==-1) //只能最后进。
              T2++;
            else
              if (Out[a]-In[a]) //怎么着都不合法了。
                T1=2;
        if (!((!T1&&!T2)||(T1==1&&T2==1))) //若不为欧拉图或半欧拉图,则退出。
        {
            cout<<"***"<<endl;
            continue;
        }
        DFS(Start);
        if (Sum!=n) //判断是否连通。
        {
            cout<<"***"<<endl;
            continue;
        }
        for (int a=Sum-1;a>=0;a--) //输出。
        {
            cout<<S[Ans[a]];
            if (a)
              cout<<".";
            else
              cout<<endl;
        }
    }
    return 0;
}

/*
    解题思路:
        其实把首尾两个字母作为节点建图,跑欧拉回路就可以了,中间的字母只是用来字典序排序的,并没有用到。
*/
原文地址:https://www.cnblogs.com/Ackermann/p/5925421.html