题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beastbeastbeast和astonishastonishastonish,如果接成一条龙则变为beastonishbeastonishbeastonish,另外相邻的两部分不能存在包含关系,例如atatat 和 atideatideatide 间不能相连。
输入输出格式
输入格式:
输入的第一行为一个单独的整数nnn (n≤20n \le 20n≤20)表示单词数,以下nnn 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
输出格式:
只需输出以此字母开头的最长的“龙”的长度
分析
这是个深搜的题,大体的思路并不难,就是不断的搜,保证每个单词出现次数不超过2次,然后每次更新res。但这个题目要仔细地看,比较考细节。这里要预处理,用一个二维数组存储单词i后接单词j的最小重叠部分的长度,这样就不用每次搜索的时候都计算一遍。要注意!!是最小重叠部分,而不是重叠部分,这应该是贪心思想,因为是求最大长度,所以当然应该取最小重叠部分。比如单词add,dda,将他们连接成addda就比adda要长。具体的预处理操作在check函数中,预处理的结果存储在re数组中。还有不可以包含:一旦包含了和不用岂不是一样。所以就只用判断上一个连接的单词和当前要连接的单词的最小重叠部分就可以了。
(另外,耽误我特别久时间的这个字符数组和string,还有char的输入问题,c++大佬绕行即可。我还没有来得及系统地学习c++,写代码很难受。。string类型用scanf输入的时候要先resize限制大小,这样我试了一下,就没法用length()获取到它的实际长度了,这里我还不知道怎么解决,留坑待填。我就先用cin来输入,cin可以直接输入,不会有任何问题。
还有一个就是关于char的输入的问题:
scanf读取int等内容时,读取完一个数据后后面的空格回车仅仅是表示这个数据输入完了,没有略过,它一直存在于输入缓冲区,而到了下一个数据,若也是int或字符串什么的,会自动把空格回车略过,直到找到应输入的内容,然后读取。而char不同,它不忽略本该读取的数据前面的空白,就把那空格或回车读进去了,如果是有多个空格回车,剩下那些空白就依然还存在与缓冲区,给下一个char读取了。
有以下几种解决方案:
使用fflush(stdin)。scanf读取char之前,用fflush(stdin)空输入缓冲区,使其为空,然后输入char就不会有空白符阻碍了。
scanf读取char之前使用getchar()把’\n’和’ ‘吃了,getchar()不跳过空格回车。当然getchar()几次,即吃几个字符也是个问题,所以本方法仅适用于比如竞赛题什么的,输入格式都很标准,不会出现不定多少的空格回车(但我用了一次之后发现这个也不好用,有个问题时windows下换行符为\r或\n,linux是\n,而MacOS是\r,你不知道后台测评的是什么系统,所以一般gets,getchar都要慎用。不过现在想想好像scanf和getchar功能一样欸,都不跳空白的。那么这么说来直接用scanf吸收空格应该也是可以的,比如scanf(“%c%c”,&ch,&ch)但是好像没见人用过。
VJ上看到这个写法scanf(“\n%c”,ch),即在每个输入char的scanf里的%c前面,都加上\n。对于在scanf里不仅仅写%c还写其他的东西,就像输出一样,我没细研究,就知道比如scanf(“233%c666”,ch)我必须先输入233再输入char再输入666,想想好像也有道理,以此来忽略\n,但是我发现它也可以忽略空格。虽然不是很懂,空格和回车在编译器眼里一样吗?于是试了试
scanf(” %c”,ch),也一样的效果!于是这就达到了与输入int、double一样的格式,任意输入多少空格和回车都没事了。现在也是这么在用,虽然其实我不是很懂其原理。)
下面是AC代码:
#include<cstdio>
#include<cstdlib>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
int n; //单词数
int res=0; //当前的最长长度
string word[25]; //存放单词
int re[25][25]; //存储两个单词的最小重叠部分
int t[25]; //记录第i个单词的剩余使用次数
int check(int x,int y){ //计算第x个单词后接第y个单词的最小重叠部分
int len1=word[x].length(),len2=word[y].length();
int a=min(len1,len2)-1;
int flag;
for(int i=1;i<=a;i++){
flag=1;
for(int j=0;j<i;j++){
if(word[x][len1-i+j]!=word[y][j]){
flag=0;
break;
}
}
if(flag) //若得到最小的重叠部分,直接返回
return i;
}
return 0;
}
void dfs(int sum,int pre){ //深搜
res=max(res,sum); //更新当前的最长长度
for(int i=0;i<n;i++){
int tmp=re[pre][i];//上一个单词后接第i个单词的最小重叠部分长度
if(tmp&&t[i]){
t[i]--;
dfs(sum+word[i].length()-tmp,i);
t[i]++; //回溯
}
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
cin>>word[i];
t[i]=2;
}
for(int i=0;i<n;i++) //预处理
for(int j=0;j<n;j++)
re[i][j]=check(i,j);
char c;
scanf(" %c",&c); //在%c前加一个空格,避免scanf将空格吃掉
for(int i=0;i<n;i++){
if(word[i][0]==c){
t[i]--;
dfs(word[i].length(),i);
t[i]++; //回溯
}
}
printf("%d\n",res);
return 0;
}
体会:要先观察能不能预处理,避免重复地计算。注意审题,慎用gets,getchar。

本文深入探讨了一个类似于成语接龙的单词接龙游戏算法,通过深度优先搜索策略实现单词链的最大长度计算,同时介绍了预处理技巧以提高效率,避免重复计算。
1676

被折叠的 条评论
为什么被折叠?



