一、题目链接
二、题目大意
给出 n n n个单词,并给出一个字符作为“头”,要求求出以“头”作为起始字符能够拼接出的字符串的最大长度,每一个单词最多使用两次。两个单词能够拼接当且仅当单词①的前缀等于单词②的后缀,则最后两个相同部分和为一个。
- b e a s t beast beast和 a s t o n i s h astonish astonish,如果接成一条龙则变为 b e a s t o n i s h beastonish beastonish
- 另外相邻的两部分不能存在包含关系,例如 a t at at和 a t i d e atide atide间不能相连(相连也没有意义)。
数据范围: 1 ≤ n ≤ 20 1\leq n\leq 20 1≤n≤20,单词长度未给出,但不超过 10000 10000 10000。
三、题目分析
因为涉及到两个单词的前后缀匹配问题,为了防止每次都去暴力寻找两个单词前后缀的匹配情况,我们可以先预处理(暴力匹配)出每两个单词的前后缀能够匹配的最大长度:
int getans(int fir,int sec)//暴力匹配fir的后缀和sec的前缀,得到最大的匹配长度
{
int Link=Max;
for(int i=1;i<len[fir];i++)
{
int flag=0;
if(len[fir]-i-1>=len[sec])
continue;
for(int j=0;j<=len[fir]-i-1;j++)
{
if(s[fir][i+j]!=s[sec][j])
{
flag=1;
break;
}
}
if(!flag)
Link=len[fir]-i;
}
return Link;
}
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
match[i][j]=getans(i,j);
因为数据范围很小,只有 n ≤ 20 n\leq20 n≤20,所以预处理结束之后我们就可以利用预处理出的结果进行搜索。因为没有明显的分层情况,无法使用 D P DP DP来求解,所以只能使用 D F S DFS DFS。如果出现一个单词没有别的单词的前缀能与之匹配,则代表到了搜索终点,记录答案的最大值即可。
四、正解程序
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define Max 0x7ffffff
using namespace std;
typedef long long ll;
int n,match[50][50],times[50],len[50];
char s[50][10010];
int ans;
int getans(int fir,int sec)//暴力匹配
{
int Link=Max;
for(int i=1;i<len[fir];i++)
{
int flag=0;
if(len[fir]-i-1>=len[sec])
continue;
for(int j=0;j<=len[fir]-i-1;j++)
{
if(s[fir][i+j]!=s[sec][j])
{
flag=1;
break;
}
}
if(!flag)
Link=len[fir]-i;
}
return Link;
}
void dfs(int k,int temp)//搜索
{
if(temp>ans)
ans=temp;
for(int i=0;i<n;i++)
{
if(times[i]<2 && match[k][i]!=Max)//能够匹配,且使用次数不超过两次
{
times[i]++;
dfs(i,temp+len[i]-match[k][i]);
times[i]--;
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>s[i];
len[i]=strlen(s[i]);
}
char Mark;
cin>>Mark;
memset(times,0,sizeof(times));
for(int i=0;i<n;i++)//求出第i个单词的后缀和第j个单词的前缀的最大匹配长度,如果不能匹配,则置为Max
for(int j=0;j<n;j++)
match[i][j]=getans(i,j);
for(int i=0;i<n;i++)
{
if(s[i][0]==Mark)//找到一个前缀为题目给定的头的单词作为搜索起点
{
times[i]++;
dfs(i,len[i]);
times[i]--;
}
}
printf("%d",ans);
return 0;
}