题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=99
题目分析:
其实上这道题把给的单词转化成一个图,然后考察这个图是否具有一个欧拉回路。
一个图具有欧拉回路的充要条件是这个图是连通的,并且只有0或2个奇点。出度比入度大一的作为起点,出度比入度小一的作为终点。
一开始我把题目抽象错了,试图去把每个单词看成一个节点,然后抽象成图,这种做法是不对的,然而这种不对的想法纠结了我很久,以致于我一直坚持WA的提交。伤心啊。正确的应该是把字符抽象出来,单独的一个字符作为一个节点,来考虑出度和入度。如果一个字符出现在字符串首,那它的出度就+1,该字符通过这个字符串可以到达字符串尾的那个字符;如果出现在字符串尾,入度加1,字符串首的字符可通过该字符串达到该字符。想了好久才想明白。
为什么要用递归,这样节省空间,并且这里的话,递归的效率挺好的。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char str[1000][31];
bool used[1000];
int In[26];
int Out[26];
int s[1000];//在Judge中用来存放每个字符串的大小Size
int stack[1000];
int n;
int compare(const void *a, const void *b)
{
char *p1 = (char *)a;
char *p2 = (char *)b;
return strcmp(p1, p2);
}
int Judge()
{
int i;
int last = -1;
int first = -1;
for(i = 0; i < 26; ++i)
{
if(In[i] != Out[i])
{
//Out为在串首出现的次数,In为串尾出现的次数
if(Out[i] - In[i] == 1 && first == -1)
first = i;
else if(Out[i] - In[i] == -1 && last == -1)
last = i;
else
return -1;
}
}
if(first > -1 && last > -1)
return first;
else if(first == -1 && last == -1)
{
for(i = 0; i < 26; ++i)
if(In[i] != 0)
return i;
}
else
return -1;
}
bool DFS(char first, int Index)//有可能有多个连通区域
{
if(Index == n)
return true;
int i;
int b,e,m;
b = 0;
e = n - 1;
while(b <= e)//二分法查找这个首字符,排序好的,肯定快啊。速度绝对比别人快2倍
{
m = (b + e)/2;
if(str[m][0] == first)
break;
else if(str[m][0] > first)
e = m - 1;
else
b = m + 1;
}
if(b > e)
return false;
//找到这个字符第一次出现的字符串
while(str[m][0] == first && m >= 0)
--m;
for(i = m + 1; str[i][0] == first; ++i)
{
if(!used[i])
{
stack[Index] = i;
used[i] = true;
if(DFS(str[i][s[i] - 1], Index + 1))
return true;
used[i] = false;
}
}
return false;
}
int main()
{
int t;
int i,first;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for(i = 0; i < n; ++i)
scanf("%s", str[i]);
memset(used, 0, sizeof(used));
memset(In, 0, sizeof(In));
memset(Out, 0, sizeof(Out));
qsort(str, n, 31 * sizeof(char), compare);
for(i = 0; i < n; ++i)
{
s[i] = strlen(str[i]);
++Out[str[i][0] - 'a'];
++In[str[i][s[i] - 1] - 'a'];
}
first = Judge();
if(first != -1 && DFS(first + 'a', 0))
{
for(i = 0; i < n - 1; ++i)
printf("%s.", str[stack[i]]);
printf("%s\n", str[stack[n - 1]]);
}
else
printf("***\n");
}
}