C语言DFS(7)___单词拼接(NYoj 99)

单词拼接

描述

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

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

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
***

有向图G 为欧拉回路:
当且仅当G 的基图连通,且所有顶点的入度等于出度。
有向图G 为欧拉路:
当且仅当G 的基图连通,且只存在一个顶点u 的入度比出度大1、
只存在一个顶点v 的入度比出度小1,其它所有顶点的入度等于出度。


分析:此题应把单词的首字母和尾字母抽离出来,首字母出度+1,尾字母入度+1,这样就可以根据每个字母的出度和入度判断起点和终点。特别的,如果是欧拉回路的话,任何一点就可以作为起点,这时候我们按照题意选择字典顺序靠前的.



#include<stdio.h>
#include<string.h>
#include<stdlib.h>

char str[1000][31];//存储字符串
bool used[1000]; //dfs的时候判断单词是否用过
int In[26];  //入度
int Out[26]; //出度
int s[1000];//在Judge中用来存放每个字符串的大小Size
int stack[1000];  //dfs存储结果
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");
	}
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值