题目背景
注意:本题为上古 NOIP 原题,不保证存在靠谱的做法能通过该数据范围下的所有数据。
本题为搜索题,本题不接受 hack 数据。关于此类题目的详细内容
题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast
和 astonish
,如果接成一条龙则变为 beastonish
,另外相邻的两部分不能存在包含关系,例如 at
和 atide
间不能相连。
输入格式
输入的第一行为一个单独的整数 𝑛表示单词数,以下 𝑛n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。
输出格式
只需输出以此字母开头的最长的“龙”的长度。
输入输出样例
输入 #1
5
at
touch
cheat
choose
tact
a
输出 #1
23
说明/提示
样例解释:连成的“龙”为 atoucheatactactouchoose
。
𝑛≤20。
参考代码:
(数据量很小,深搜速度很快)
觉得不难的这个值得一试
【想要挑战极限吗】
#include<bits/stdc++.h>
#define int long long
using namespace std;
string s[25],sd;
int n,vis[25],k[25][25];
int dfs(int j)//深搜求最长单词长度
{
int as=0;
for(int i=1;i<=n;i++)//枚举
{
if(vis[i]<=1)//一个单词只能用两次
{
int x=k[j][i];//i接在j后面最小重叠部分
if(x!=0)//i可以接在j后面
{
vis[i]++;//i的使用加1
//cout<<j<<" "<<i<<'\n';
as=max(as,x+dfs(i));//统计下一个单词长度
vis[i]--;//回溯
}
}
}
return as;//输出这次答案的单词长度
}
int qiu(int h,int g)//求取g在h后面的最小重叠部分
{
int c[25];
int js=0;
int len1=s[h].size();//h的长度
int len2=s[g].size();//g的长度
for(int i=0;i<len1;i++)
if(s[h][i]==s[g][0])//将g的首字符与h的字符进行匹配
c[++js]=i;
for(int i=js;i>=1;i--)//求g在h后面的最小重叠部分
{
int f=c[i];//首字符
int j=0;
while(f<=len1-1&&j<=len2&&s[h][f]==s[g][j])//查找h与g是否匹配
{
f++;j++;
}
if(f==len1&&j!=len2)//当g的一部分前面与h的后面匹配且不包含g这个字符串
{
if(c[i]>0)//g不包含h这个字符串
return len2-j;//输出g字符串除重叠部分的剩余部分字符数量
}
}
return 0;//无法匹配
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
int ans=0;
for(int i=1;i<=n;i++)
cin>>s[i];//保存字符串
cin>>sd;//开头
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
k[i][j]=qiu(i,j);//求取j在i后面的重叠部分;
// for(int i=1;i<=n;i++)
// for(int j=1;j<=n;j++)
// if(i!=j)
// cout<<i<<" "<<j<<" "<<k[i][j]<<'\n';
for(int i=1;i<=n;i++)
{
if(sd[0]==s[i][0])//寻找首字符
{
vis[i]=1;//使用过一次
int d=s[i].size();//加上第一个字符串长度
ans=max(ans,d+dfs(i));//以此字符串开头的字符总长度
vis[i]=0;//回溯
}
}
cout<<ans;//输出答案
return 0;
}