题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beastbeast和astonishastonish,如果接成一条龙则变为beastonishbeastonish,另外相邻的两部分不能存在包含关系,例如atat 和 atideatide 间不能相连。
输入输出格式
输入格式:
输入的第一行为一个单独的整数nn (n \le 20n≤20)表示单词数,以下nn 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
输出格式:
只需输出以此字母开头的最长的“龙”的长度
输入输出样例
输入样例#1:
5 at touch cheat choose tact a
输出样例#1:
23
说明
(连成的“龙”为atoucheatactactouchoose)
思路截取自洛谷题解https://www.luogu.org/problemnew/solution/P1019
基本思路是搜索。
处理的难点在于对重叠部分的处理。
单词的使用次数很好判断,开一个数组即可,和正常向dfs的vis数组差不多。
但对于重叠部分的处理,我想细说一下。
因为连接起来的单词要最长,所以对比是选择从上一个单词的末尾与当前单词的开头进行比对,如果发现不符那就不能匹配。
这里我借鉴了一位大神的思路,使用一个check函数,用来比较两个串s和m的长度为k的接口能不能匹配。
判断方式有两种,第一种就是我用的这样按字符比较,如果发现某处不匹配立即返回false。还有一个方法是用string里面的substr,我就不展开了,有兴趣的同学可以试一下。
我们假设接口长度为k,串s的长度为lens,然后我们从0到k枚举,判断s[lens-k+i]是不是等于m[i]。
这个式子怎么来的呢?接口前面的串是s,后面的串是m,那么很显然,s串的接口最开始应该是lens-k处,然后在后面加上一个枚举的i就可以保证扫描到所有接口字符(我们的i是从0开始枚举的)。
还有一些细节问题。 如果我们在接龙的时候发现我们现在要接的龙还不如之前某一次接过的长,那么这个接龙方案肯定不是最优的,所以要舍去,这个正确性是显然的。
(想一下深搜时的遍历过程,这句话便不难理解)
使用我这个方法,可能有一个奇怪的想法有同学没想到,那就是在进行拼接操作时,要注意使用给定的串的副本(即复制一份原来的串)进行拼接处理。
这是因为,如果你把原串改变了,而且这个串还不是最优的,那就完了,回溯不回去了。
具体到操作上,首先,我们从1到n枚举每个短字符串,如果它已经被用了两次则continue,然后我们求出当前短串的长度,从1到这个长度枚举,枚举的是接口的长度(自然是接口越短融合串越长嘛) 然后执行拼接操作,记录一下最大长度,再加上回溯就好了。
拼接操作和check函数很像,具体到代码上大家就能看明白了。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
string words[25],begin;
int ans,n,used[25];
bool check(string a,string b,int k)
{
int len=a.length();
for(int i=0;i<k;i++)
{
if(a[len-k+i]!=b[i]) return false;
}
return true;
}
void append(string &a,string b,int k)
{
int len=b.length();
for(int i=k;i<len;i++)
{
a+=b[i];
}
}
void dfs(string nowString)
{
int nowLen=nowString.length();
ans=max(nowLen,ans);
for(int i=0;i<n;i++)
{
if(used[i]>=2) continue;
int maxk=words[i].length();
for(int j=1;j<maxk;j++)
{
if(check(nowString,words[i],j))
{
string temp=nowString;
append(temp,words[i],j);
if(temp==nowString) continue;
used[i]++;
dfs(temp);
used[i]--;
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
cin>>words[i];
}
cin>>begin;
dfs(begin);
printf("%d",ans);
return 0;
}