解题思路:
(1)本题的细节处理比较多,题意比较好理解,但是理解容易出错
(2)首先,给定的单词可以利用一个string数组来存储,然后输入首字母,第一步应该是在所有单词中,找到以这个字符开头的单词,作为龙的第一项,然后开始dfs深搜
(3)因为每个单词最多可以使用2次,所以建立一个桶数组,用来标记每个单词使用的次数即可,其次单词拼接不能存在包含关系(本人理解错误意思),最后单词必须有重合的部分才可以接下去,并且重合的部分不算长度。
(4)因为要找所有情况中的最佳方案,那么需要用到回溯,直接利用计数的桶数组回溯即可
(5)dfs(string a,int len)记录此时龙的末尾单词,因为要找下一个可以拼接的单词,所以必须得保存这个单词,并且记录此时的长度,每次都保存好,用来求最大值。再进行深搜的时候,无非就是又把所有的单词重新组合一遍,看哪些可以组合,然后加上长度,注意:这里的长度是需要减掉重合部分的。
(6)那么可以设置一个check函数来判断两个字符串的重合长度,因为理解错意思,题目中的相连的两个单词不能产生包含关系,所以开头就直接判断了是否包含,导致没有AC,其实题目的意思为,如果不能使龙的长度增加,则不能包含,因为包含进去就是重合的部分,和没加一样(去你码的,谁能想到)比如ata和atatata是可以相连的
(7)还有一个问题,当找到重合的字母的时候,到底要找到最长的公共部分,还是找到越短的呢?比如上一个是aa下一个单词是aaaaa,如果按照最短的拼接,形成的长度为六个a,如果按照公共的长度,那么还是5个a(其他博主说是6个a,只要找到一个首字母即可,有待钻研,AC代码是按照最短的来,找到了一个相同的,return i即可)
(8)然后就是dfs函数,等到所有的情况枚举完之后,便退出,每次深入一层,就取长度的最大值即可,注意这里的可行性方案为单词有重合,而且单词不能使用超过两次。
#include<bits/stdc++.h>
using namespace std;
int n,ans;
char ch;//表示首字母
string a[30];//存入字符串
int vis[30];//桶数组标记
int check(string s1,string s2)
{
int len1=s1.length();//获取之前字符串的长度
int len2=s2.length();//获取待接的字符串的长度
int mine=min(len1,len2);//求出最短长度
for(int i=1;i<=mine;i++)
{
string ss1=s1.substr(len1-i,i);//截取尾部的字符串
string ss2=s2.substr(0,i);//截取首部的字符串
if(ss1==ss2)//如果这两个字符串相等
return i;//返回重合的长度
}
return 0;//如果没有找到重合的,返回0
}
void dfs(string s,int len)
{
ans=max(ans,len);//取最长的龙
for(int i=1;i<=n;i++)
{
if(check(s,a[i])!=0&&vis[i]<2)//可行性方案,如果可以连接并且单词没有使用两次以上
{
vis[i]++;//使用该单词数量+1,记录现场
dfs(a[i],len+a[i].length()-check(s,a[i]));//长度增加a[i]的长度,并且减去重合部分
vis[i]--;//恢复现场
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
cin>>ch;//输入数据
for(int i=1;i<=n;i++)//遍历第一个可以连接的单词
{
string ss=a[i];
if(ss[0]==ch)//如果首字母和给定字符相同
{
memset(vis,0,sizeof(vis));//每次清空桶数组
vis[i]=1;//第i个单词用一次
dfs(ss,ss.length());//开始搜索
vis[i]=0;//回溯
}
}
cout<<ans;
return 0;
}