题目描述
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母(不是单词),请你计算以这个字母开头的长度最长的“龙”,每个单词最多在“龙”中出现两次。
要注意的是,两个单词接龙规则如下:
- 如果第一个单词的后面的连续若干字母与第二个单词前面的连续若干字母依次相同,则这两个单词可以接龙,例如 beast 和 astonish ,如果接成一条龙则变为 beastonish。
- 两个单词接成一条龙后,龙的长度要比两个单词的长度都长。例如单词 at 和 atide 接成一条龙后为atide,龙的长度与第二个单词相同,所以这两个单词不能接龙。而单词ababab和ababab,可以接成ababababab,但不能为ababab。
- 接龙时,如果两个单词接龙后的单词有多种情况,要尽可能保证接龙后龙的长度最长。例如cabab和ababc,一定要接成cabababc,为而不是cababc
输入格式
输入的第一行为一个单独的整数 n 表示单词数,以下 n 行每行有一个单词(只含有大写或小写字母,长度不超过20),输入的最后一行为一个单个字符,表示“龙”开头的字母。
你可以假定以此字母开头的“龙”一定存在。
输出格式
只需输出以此字母开头的最长的“龙”的长度。
数据范围
n
≤
20
n≤20
n≤20
输入样例:
5
at
touch
cheat
choose
tact
a
输出样例:
23
提示
连成的“龙”为 atoucheatactactouchoose。
算法思想 DFS
从数据范围 n < = 20 n<=20 n<=20可以考虑使用DFS算法,尝试所有以“龙”字母开头的单词进行接龙,在搜索中计算最长的“龙”的长度。
注意:
- 为了能快速判断出两个单词是否能够“接龙” ,可以预先将所有单词之间的关系
g[i][j]
,g[i][j] = 3
表示第i
个单词和第j
个单词可以接龙,且相同部分的长度为3
。 - 为了保证每个单词最多在“龙”中出现两次,可以使用
used[i]
记录单词使用次数,例如:used[i] = 2
表示第i
个单词使用了两次。
代码实现
#include <iostream>
using namespace std;
const int N = 25;
int n, ans;
string word[N];
//g[i][j]表示单词i和单词j是否可以相连
//如果可以,重合部分的最小长度是多少
int g[N][N];
//used[i]表示单词i的使用次数
int used[N];
//dragon当前龙,上次使用的单词编号
void dfs(string dragon, int last)
{
ans = max(ans, (int)dragon.size());
//单词last使用次数增加1次
used[last] ++;
for(int i = 0; i < n; i ++)
{
//单词i使用不足两次,并且单词last和i能够连接
if(used[i] < 2 && g[last][i])
//新龙为dragon + word[i].substr(g[last][i]),使用的是单词i
dfs(dragon + word[i].substr(g[last][i]), i);
}
used[last] --; //回溯
}
int main()
{
cin >> n;
for(int i = 0; i < n; i++) cin >> word[i];
char st;
cin >> st;
//处理所有单词是否可以相连
//如果可以,则重合部分的最小长度是多少
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
{
string a = word[i], b = word[j];
//枚举所有长度,注意这里要严格小于两个串中最小的长度
for(int k = 1; k < min(a.size(), b.size()); k++)
{
//如果a的后缀和b的前缀相等
if(a.substr(a.size() - k, k) == b.substr(0, k))
{
g[i][j] = k;
//可以任意选择重合部分的长度,但其长度必须大于等于1,
//注意,这里找到后中断,保证重叠的部分最小
break;
}
}
}
for(int i = 0; i < n; i++)
{
if(word[i][0] == st)
dfs(word[i], i);
}
cout << ans << endl;
return 0;
}