洛谷题单指南-搜索-P1019 [NOIP2000 提高组] 单词接龙
原题链接:https://www.luogu.com.cn/problem/P1019
题意解读:要计算接龙能得到的最长字符串,可以通过DFS暴搜所有可能的接龙方案
解题思路:
DFS的关键在于两个判断:
1、下一个单词是否可以和上一个单词接龙,最短公共长度是多少(只需要看两个单词的最短公共长度,这样能保证接龙更长)
2、单词是否已接龙两次
判断2通过int flag[N]即可解决,下面重点讨论判断1如何处理
设int conn[i][j]表示在第i个单词后接第j个单词时,最少的公共长度,如果为0说明不能接
通过双重循环枚举所有单词,可以得到两两之间的最少公共长度
具体说来:
只需要针对两个单词s1、s2,从1开始枚举公共长度k(不能超过任意字符串的长度,因为不能存在包含关系)
判断s1的后k位是否和s2的前k位相同,找到则保存最小的k,详细过程请参考代码。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 25;
int conn[N][N]; //conn[i][j]表示第i个单词后接第j个单词时,最少的公共长度,如果为0说明不能接
string s[N]; //所有单词
int flag[N]; //每个单词被用了多少次
int n;
char first;
int ans;
//str:接龙 idx:上一个接上的单词
void dfs(string str, int idx)
{
ans = max(ans, (int)str.length());
for(int i = 1; i <= n; i++)
{
if(conn[idx][i] > 0 && flag[i] < 2) //如果存在可接龙的单词
{
string longstr = str; //接龙后的字符串
for(int j = conn[idx][i]; j < s[i].length(); j++)
{
longstr += s[i][j]; //接龙操作
}
flag[i]++;
dfs(longstr, i); //继续找下一个可接龙的单词
flag[i]--;
}
}
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++) cin >> s[i];
cin >> first;
//计算每两个单词相接的最短公共长度,由于不能存在包含关系,公共长度要小于长度较小的字符串
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int k = 1; k < min(s[i].length(), s[j].length()); k++) //枚举公共长度可能的值
{
bool isk = true; //公共长度是否是k
string s1 = s[i]; int start1 = s1.length() - k;
string s2 = s[j]; int start2 = 0;
for(int l = 1; l <= k; l++)
{
if(s1[start1] != s2[start2])
{
isk = false;
break;
}
start1++; start2++;
}
if(isk)
{
conn[i][j] = k;
break; //找到最短的公共长度,就应该结束,这样才能保证接上的龙最长
}
}
}
}
for(int i = 1; i <= n; i++)
{
if(s[i][0] == first)
{
memset(flag, 0, sizeof(flag)); //从每一个可能的单词开始dfs,都需要重置flag
flag[i]++;
dfs(s[i], i);
}
}
cout << ans;
return 0;
}