NYOJ 99 单词拼接

单词拼接

时间限制: 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的单词,单词全部由小写字母组成。
输出
如果存在拼接方案,请输出所有拼接方案中字典序最小的方案。(两个单词之间输出一个英文句号".")
如果不存在拼接方案,则输出

***


这是一个图论问题,比如我们可以把单词看成边或点,如果我们把每个英文字母看成一个点,那每个单词就成了连接两个英文字母的边,于是问题就是是否存在一条路径,可以遍历每一条边且无重复,如果最终能回到原点,就是欧拉回路问题,否则,就是欧拉路问题。

对于有向图来说:

存在欧拉回路等价于任意顶点的出度入度相等,对本题,即对于26个英文字母中的任意个,以之开头的单词和与之结束的单词数总是相等。这时,如果我们沿着任意一个顶点随意前进,当某个点再也没有边可走时,会发现这个点还是我们出发时的那个点。

存在欧拉路等价于只有一个定点的出度大于入度,把这个点记为a,还有一个定点入度大于出度,记为b,其余皆相等。如果我们沿着a以任意的边走,当再也没有边可走时,此时一定是b点。



以上是一个欧拉回路,我们随意从a开始走,比如是a-b-c-d-e-a,这时候没路了,我们倒着看,发现c点上还有边,于是继续c-f-g-h-c又是一个圈,把这个圈插入上个圈,就构成了欧拉回路这也是信息学竞赛书中介绍的套圈法的原理,但是如果我们按照最小字典序找边,就会以最小字典序输出。这是个递归的过程。


 

 
#include <cstdio>
#include <cstdlib>
#include <cstring>
char* pword[1003];
char word[1003][31],lc[1003],c;
int map[26][1003];
int a,b,stack[1003],M;
struct hehe{
	int num;
	hehe *pre;
}temp,*ptr;
int com(const void *a,const void *b){
	return strcmp(*(char **)a,*(char**)b);
}
void sort(int num){
	int i=1,large,left,right,j;
	do{
		j=0;
		left=2*i;right=left+1;large=i;
		if(left<=map[num][0]&&map[num][left]<map[num][i])				large=left;
		if(right<=map[num][0]&&map[num][right]<map[num][large])	large=right;
		if(i!=large){
			b=map[num][i];map[num][i]=map[num][large];map[num][large]=b;
			i=large;
			if(2*i<=map[num][0])	j=1;
		}
	}while(j);
}
void dfs(int num){
	hehe *last;
	int i=num;
	ptr=NULL;
	while(map[i][0]!=0){
		M--;
		last=(hehe*)malloc(sizeof(hehe));
		last->num=map[i][1];
		last->pre=ptr;
		ptr=last;
		a=lc[map[i][1]]-'a';
		map[i][1]=map[i][map[i][0]--];
		sort(i);i=a;
	}
	while(last!=NULL){
		a=pword[last->num][0]-'a';
        stack[++stack[0]]=last->num;
        ptr=last;last=last->pre;free(ptr);
        if(map[a][0]!=0)
            dfs(a);
	}
}
int main(){
	int N;scanf("%d",&N);
	while(N--){
		scanf("%d",&M);
		for(int i=0;i<M;i++){
			scanf("%s",word[i]);
			pword[i]=word[i];
		}
		for(int i=0;i<26;i++)   map[i][0]=0;
		qsort(pword,M,sizeof(char*),com);
		for(int i=0;i<29;i++)	stack[i]=0;
		b=25;
		for(int i=0;i<M;i++){
			a=pword[i][0]-'a';map[a][++map[a][0]]=i;lc[i]=pword[i][strlen(pword[i])-1];
			stack[a]++;stack[lc[i]-'a']--;
			if(a<b)	b=a;
		}
		for(int i=0;i<26;i++){
			if(stack[i]==-1){stack[26]++;continue;}
			if(stack[i]==1){stack[27]++;continue;}
			if(stack[i]==0)	stack[28]++;
		}
		a=0;
		if(stack[28]==26||(stack[26]==1&&stack[27]==1&&stack[28]==24))		a=1;
		if(!a)	{printf("***\n");continue;}
		for(int i=0;i<26;i++){
			if(stack[i]==1)	b=i;
		}
		stack[0]=0;dfs(b);
		if(M)
            {printf("***\n");continue;}
		b=stack[0];
		printf("%s",pword[stack[b]]);
		for(int i=b-1;i>0;i--)  printf(".%s",pword[stack[i]]);
		printf("\n");
    }
	return 0;
}
        


以上用到了二叉堆优化,在sort()函数中体现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值