单词拼接
时间限制:
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()函数中体现。
-
第一行是一个整数N(0<N<20),表示测试数据的组数