题目链接:洛谷1019
题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast和astonishastonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at 和 atide 间不能相连。
输入格式
输入的第一行为一个单独的整数n (n≤20)表示单词数,以下n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
输出格式
只需输出以此字母开头的最长的“龙”的长度
输入输出样例
输入 #1
5
at
touch
cheat
choose
tact
a
输出 #1
23
题解:
这道题的题目描述有些模糊,澄清一下两点:
1. 字符串总长度并不长,暴力跑一遍没问题;
2. 在连接字符串时,应取最小总长度以保证合并后长度最长;
3. 能连接的字符串必须有公共部分,没有不能连接;
4. 每个字符串最多用两遍,但可以不用完。
算法显然搜索,选择DFS,因为记录状态较麻烦,但BFS并非不可。首先预处理出两个字符串连接时的最小公共长度。记i后接j的最小公共长度为same[i][j],这里需要注意的是,自己是可以接自己的。然后进行DFS就好啦。上代码。
代码:
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int maxn=25;
int n,totlen,ans=-1;
int used[maxn];
int same[maxn][maxn];
string head;
string words[maxn];
void DFS(int i,int depth){
ans=max(ans,totlen);//更新
for (int j=1;j<=n;j++){
if (same[i][j] && used[j]!=2){
used[j]++,totlen+=words[j].length()-same[i][j];
DFS(j,depth+1);
used[j]--,totlen-=words[j].length()-same[i][j];//记得还原
}
}
}
int main(){
// freopen("words.in","r",stdin);
// freopen("words.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;
for (int i=1;i<=n;i++) cin>>words[i];
cin>>head;
预处理出每两个字符串连接时的最小公共长度
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
int leni=words[i].length(),lenj=words[j].length(),maxx;
for (int len=min(leni,lenj);len>=1;len--)//注意要倒序
if (words[i].substr(leni-len,len)==words[j].substr(0,len)) same[i][j]=len;
if (same[i][j]==min(leni,lenj)) same[i][j]=0;//如果包含了是不能连接的
}
for (int i=1;i<=n;i++)
if (words[i].substr(0,1)==head){
totlen+=words[i].length(),used[i]++;
DFS(i,1);
totlen-=words[i].length(),used[i]--;
}
cout<<ans;
return 0;
}