原题链接:https://www.luogu.org/problem/P1019
题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast和astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at 和 atide 间不能相连。
输入输出格式
输入格式:
输入的第一行为一个单独的整数n (n ≤ 20)表示单词数,以下n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
输出格式:
只需输出以此字母开头的最长的“龙”的长度
输入输出样例
输入样例#1:
5
at
touch
cheat
choose
tact
a
输出样例#1:
23
说明
时空限制:1000ms 125M
(连成的“龙”为atoucheatactactouchoose)
NOIp2000提高组第三题
思路:
1、一道dfs+字符串的题目。
2、先用数组used[i]把所有单词初始化为0,表示所有单词均未使用过。然后从给定的字符头进行深搜,遍历每个单词,先判断该单词使用次数是否已满两次,再通过自定义check函数判断该单词是否符合条件能拼接上,如果可以则调用自定义split函数拼接单词。然后判断长度改变,是的话可以连接,继续深搜,否则回溯,具体见代码注释。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int n; //单词数
int ans=0; //最长的"龙"的长度
int used[25]; //表示单词用过的次数
string word[25],start; //表示单词和开头的字母
bool check(string str3,string str4,int t) //检查两个单词是否符合拼接条件
{
int len3=str3.length(); //前面单词长度
for(int i=0;i<t;i++) //遍历接口长度
{
if(str3[len3+i-t]!=str4[i]) //如果后面单词前缀与前面单词后缀不同
return false; //返回false
}
return true; //如果后面单词前缀与前面单词后缀相同,返回true
}
void split(string &str3,string str4,int t) //拼接
{
int len4=str4.length(); //后面单词长度
for(int i=t;i<len4;i++) //把后面单词重合部分忽略掉,拼接不重合部分
str3+=str4[i];
}
void dfs(string str1) //深搜
{
int len1=str1.length(); //拼接的前面单词长度
ans=max(len1,ans); //每次拼接之后取最大值
for(int i=0;i<n;i++)
{
if(used[i]>=2) //每个单词最多使用两次
continue;
else //如果单词未使用满两次
{
int len2=word[i].length(); //拼接的后面单词长度
for(int j=1;j<=len2;j++) //枚举后面单词作为接口的长度
{
if(check(str1,word[i],j)) //如果符合拼接条件拼接
{
string str2=str1; //把前面单词暂存进str2
split(str2,word[i],j); //拼接
if(str2==str1) //如果拼接之后长度没改变,说明两个单词包含关系
continue; //剪掉
used[i]++; //单词使用次数+1
dfs(str2); //继续深搜
used[i]--; //回溯
}
}
}
}
}
int main()
{
memset(used,0,sizeof(used)); //表示单词均未用过
scanf("%d",&n);
for(int i=0;i<n;i++) //输入单词
cin>>word[i];
cin>>start; //输入开头的字母
dfs(start); //从开头的字母开始深搜
printf("%d\n",ans); //输出"龙"的长度
return 0;
}