NYOJ 99 单词拼接 欧拉回路

探讨如何通过判断一组单词是否能首尾相连形成字典序最小的串接方案,利用图论中的欧拉回路概念实现算法设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单词拼接

时间限制:3000 ms  |  内存限制:65535 KB
难度:5
描述

给你一些单词,请你判断能否把它们首尾串起来串成一串。

前一个单词的结尾应该与下一个单词的道字母相同。

aloha

dog

arachnid

gopher

tiger

rat

 

可以拼接成:aloha.arachnid.dog.gopher.rat.tiger

输入
第一行是一个整数N(0<N<20),表示测试数据的组数
每组测试数据的第一行是一个整数M,表示该组测试数据中有M(2<M<1000)个互不相同的单词,随后的M行,每行是一个长度不超过30的单词,单词全部由小写字母组成。
输出
如果存在拼接方案,请输出所有拼接方案中字典序最小的方案。(两个单词之间输出一个英文句号".")
如果不存在拼接方案,则输出
***
样例输入
2
6
aloha
arachnid
dog
gopher
rat
tiger
3
oak
maple
elm
样例输出
aloha.arachnid.dog.gopher.rat.tiger
***

如果满足题意,那么数据一定可以构成欧拉回路,欧拉回路的条件是: 所有点的入度和出度相等;欧拉路径的条件是:满足欧拉回路条件或者图中最多有一个点入度比出度大1,至多有一个点出度比入度大一,如果满足以上条件,再进行dfs深搜找出一条路径,然后输出,如果找不到则不存在

实上这道题把给的单词转化成一个图,然后考察这个图是否具有一个欧拉回路。
一个图具有欧拉回路的充要条件是这个图是连通的,并且只有0或2个奇点。出度比入度大一的作为起点,出度比入度小一的作为终点。
把字符抽象出来,单独的一个字符作为一个节点,来考虑出度和入度。如果一个字符出现在字符串首,那它的出度就+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");
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值