目录
题目
题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast
和 astonish
,如果接成一条龙则变为 beastonish
,另外相邻的两部分不能存在包含关系,例如 at
和 atide
间不能相连。
输入格式
输入的第一行为一个单独的整数 表示单词数,以下 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。
输出格式
只需输出以此字母开头的最长的“龙”的长度。
输入输出样例
输入样例#1
5
at
touch
cheat
choose
tact
a
输出样例#1
23
说明/提示
样例解释:连成的“龙”为 atoucheatactactouchoose
。
思路
一. 审题
从题目条件当中,我们可以得出以下条件:
1.每个单词最多用2次。
2.有的单词自己可以连接自己(比如说"tact",它可以以"t"为重合部分进行连接)。
3.重合部分越小越好。
4.两个单词之间不能存在包含关系(也就是说最小重合部分不能为这两个单词中较短单词的长度)。
二. 构成
本题的代码可以分成以下部分
①输入个单词
②执行查找每两个单词之间的重合部分的函数
③运用DFS搜索来“接龙”,找出“龙”的最大长度
1.主函数
我们使用 来存储这个单词,之后输入,再分别输入这个单词,之后,我们用执行函数来查找两个单词(可以为同一个单词)之间的重合部分,并用来表示第个和第个单词之间的重合部分,再在这个单词中寻找开头为“龙头”的单词,如果找到了,就开始DFS来“接龙”寻求最大长度。
2.dra函数
这里以字符串与字符串为例。
我们从1开始,不断加大假设的重合部分的长度,将字符串中以倒数第个字母为开头的长度为的字串与字符串中以第0个字母(毕竟输入字符串时第一个字符的位置一般为0)为开头的长度为的字串进行比较,如果相符,就直接判定这个字串是字符串与字符串的重合部分(因为字串长度要越小越好,所以找到重合部分就立即停止),但是要先检查字串的长度是否等于两个字符串之间较短的字符串的长度,如果等于,那么就说明这两个字符串存在包含关系,并将其视作为没有重合部分,否则就这个字串的长度作为返回值赋值于。
关于字串,这里要简单介绍一个字符串函数:
就是用来表示从某个字符串的某个位置为开始,长度为某个数值的字串,格式为:
字符串.substr(字串第一个字符在该字符串的位置,字串的长度);
3.DFS
在开始DFS之前,要用数组来表示这个第个单词被使用的次数,用整形变量表示“龙”的最大长度(注意:刚才提到的要建立的数组和变量是全局变量),除了在主函数部分提到的寻找“龙头”,还需要将这个单词的使用次数增加到1,避免出现该单词的第三次使用,在DFS执行过后,还要将之前增加的1次使用次数减去,以免后患。
接下来,才是正式的DFS函数部分:
由于我们最终改变的数值是全局变量,所以我们将返回值类型定义为void,参数设定为选中的单词的序数,以及“龙”的长度。
首先,选中“龙头”的单词,传递参数为“龙头”单词的序数(作为最开始的选中的单词)和该单词的长度(作为最开始的“龙”的长度),之后,递归执行以下内容:
首先比较“龙”的长度与最大长度,如果,那么,之后我们查找个单词中(包含选中的单词)使用次数不为2且与选中单词的重合部分长度不为0的单词,将其选中“接龙”,把目前“龙”的长度加上刚刚选中的单词的长度并减去重合部分的长度,就是现在“龙”的长度,之后调用DFS函数,传递参数为刚才选中的单词的序数以及目前“龙”的长度,重复上述操作直到没有可选的单词(循环结束),再进行return; 回到上一层递归。
注:你现在可以选择根据上面的条件写出代码。
代码
一. 部分代码实现
1.主函数及全局变量
string s[21];//表示n个单词
char c;//表示“龙头”的字符
int g[21][21],n,f[21],maxl;//g[i][j]表示第i个单词与第j个单词的重合部分,f[i]表示第i个单词的使用次数,maxl表示“龙”的最大长度
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s[i];
}//输入n个单词
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
g[i][j]=dra(s[i],s[j]);
}
}//利用dra函数查找第i个单词与第j个单词之间重合部分的长度(这里i可以等于j,因为可以“自己连自己”)
cin>>c;//输入“龙头”
for(int i=1;i<=n;i++)
{
if(s[i][0]==c)//查找n个单词中有没有可以作为“龙头”的单词)
{
f[i]=1;//以免出现第三次使用这个单词的现象
dfs(i,s[i].size());//dfs(选中的单词的序数,“龙”的长度(此处为初始长度))
f[i]=0;//以免后患
}
}
cout<<maxl;//输出最大长度
return 0;
}
2.dra函数
int dra(string a,string b)
{
int result=0;//重合长度
for(int i=1;i<=min(a.size(),b.size());i++)//i表示假设重合部分的长度
{
if(a.substr(a.size()-i,i)==b.substr(0,i))//两个字串(见上文内容,这里不做赘述)
{
result=i;
break;//找到重合部分立即退出(为了得到最短的重合部分长度)
}
}
if(result==min(a.size(),b.size()))return 0;//如果重合部分长度为两字符串较短的字符串的长度(即存在包含关系),就视作没有重合部分。
else return result;//否则照常返回
}
3.DFS
void dfs(int t,int l)//t:选定的单词的序数,l:“龙”的长度
{
if(l>maxl)maxl=l;//如果这里没有判断,即使是只有有最长的“龙头”能作为最大长度(无单词可接),也会输出0,不符合题意。
for(int i=1;i<=n;i++)
{
if(f[i]!=2&&g[t][i]!=0)//寻找能够选定的单词(使用次数不为2,且有重合部分)
{
if((l+s[i].size()-g[t][i])>maxl)maxl=l+s[i].size()-g[t][i];//“接龙”后“龙”的长度,每到“龙”的长度发生改变就要判断是否为最大长度
f[i]++;//增加使用次数,以防超过2次使用
dfs(i,(l+s[i].size()-g[t][i]));//递归
f[i]--;//消减使用次数,以防出现实际低于2次使用
}
}
return;//没有单词可以使用时,返回到上一层递归
}
二. 最终代码
#include<bits/stdc++.h>
using namespace std;
string s[21];
char c;
int g[21][21],n,f[21],maxl;
void dfs(int t,int l)
{
if(l>maxl)maxl=l;
for(int i=1;i<=n;i++)
{
if(f[i]!=2&&g[t][i]!=0)
{
if((l+s[i].size()-g[t][i])>maxl)maxl=l+s[i].size()-g[t][i];
f[i]++;
dfs(i,(l+s[i].size()-g[t][i]));
f[i]--;
}
}
return;
}
int dra(string a,string b)
{
int result=0;
for(int i=1;i<=min(a.size(),b.size());i++)
{
if(a.substr(a.size()-i,i)==b.substr(0,i))
{
result=i;
break;
}
}
if(result==min(a.size(),b.size()))return 0;
else return result;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s[i];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
g[i][j]=dra(s[i],s[j]);
}
}
cin>>c;
for(int i=1;i<=n;i++)
{
if(s[i][0]==c)
{
f[i]=1;
dfs(i,s[i].size());
f[i]=0;
}
}
cout<<maxl;
return 0;
}
AC!