洛谷,https://www.luogu.com.cn/problem/P1019。
计蒜客,https://nanti.jisuanke.com/t/T2110。
计蒜客,https://nanti.jisuanke.com/t/T2152。
我的OJ,http://47.110.135.197/problem.php?id=4209。
单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast 和 astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如 at 和 atide 间不能相连。
输入的第一行为一个单独的整数 n (n≤20) 表示单词数,以下 n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。
只需输出以此字母开头的最长的“龙”的长度。
5
at
touch
cheat
choose
tact
a
23
最长的龙是 atoucheatactactouchoose。
在给点单词中,找出最长的龙。注意每个单词在龙中最多出现两次。
比如样例数据中的 tact 这个单词,正好是首字母和尾字母都为 t,按照接龙规则,是可以自己接自己的。
我们根据样例数据,可以绘制出下图所示的关系:
如上图所示,要找出最长的单词,自然是一个 DFS 过程,也就是从 at 出发写出所有的 DFS 可能,找到最长的那个即可。能找到的满足要求的 DFS 遍历可能有以下几种:
atoucheatoucheatactact 22
atoucheatouchoose 17
atoucheatactoucheatact 22
atoucheatactouchoose 20
atoucheatactactoucheat 22
atoucheatactactouchoose 23
atouchoose 10
atactoucheatoucheatact 22
atactoucheatouchoose 20
atactoucheatactoucheat 22
atactoucheatactouchoose 23
atactouchoose 13
atactactoucheatoucheat 22
atactactoucheatouchoose 23
atactactouchoose 16
可以看出上面最长的长度为 23。
1、读取数据。
2、找龙头。
3、构造变化关系数据。
4、遍历所有龙头,用当前龙头进行 DFS,得到所有 DFS 可能,找出最长的数据。
注意,需要找最小的重合长度。比如一个单词为 abababab 和另外一个单词 abababab 进行接龙,重合字符应该是 2 个,即 ab,接龙后的词为 ababababababab。
由于本题数据量小,n ≤ 20,所以使用暴力比较是可以的。也就是每次比较字符串 a 和字符串 b 的第 k 个位置能否接龙,即实现一个如下的函数:
bool check(const string &a, const string &b, int k)
维护一个关系表。如同上面的图片,我们在读入用户数据后,生成一个关系表。这个表示描述字符串 a 后面接上字符串 b 将重合几个字符,可以考虑使用二维数组来描述。参考有向图,比如样例输入,我们可以得到如下表所示的关系,考虑初始化问题,0 表示两个单词不能接龙,x>0 表示可以第 i 个单词尾部接上第 j 个单词将重复 x 个字符。
单词 | at | touch | cheat | choose | tact |
at | 0 | 1 | 0 | 0 | 1 |
touch | 0 | 0 | 2 | 2 | 0 |
cheat | 0 | 1 | 0 | 0 | 1 |
choose | 0 | 0 | 0 | 0 | 0 |
tact | 0 | 1 | 0 | 0 | 1 |
上表什么意思呢?a[i][j] 表示第 i 个字符串后面接第 j 个字符串将重合几个字符。比如 a[1][2]=2,表示字符串 touch 后面如果要接上字符串 cheat,将重复 2 个字符,那么接龙后的单词为 toucheat,长度为 5(touch长度) + 5(cheat长度) - 2(重合长度) = 8。
1、获取第 i 个单词和第 j 个单词长度的最小值,用 tt 表示。
2、如果 i==j,说明是同一个单词,则 tt 减一。因为自己不能
3、开一个循环,k 从 1 开始到 tt 结束。第 i 个单词从后向前取 k 个字符,第 j 个单词从前向后取 k 个字符,如果这 k 个字符相同,说明第 i 个单词后接上第 j 个单词构成的龙,将有 k 个字符是重复的。
4、循环结束后,判断一下包含关系。如果计算出重复的字符和 tt 大小相同,说明包含关系。
代码实现如下:
//找龙头和构建重复字符表
vector head;
for (int i=0; i
#include
#include
#include
using namespace std;
const int MAXN = 20+2;
const int MAXUSED = 2;
string word[MAXN];//单词
int used[MAXN];//单词已经使用次数
int relation[MAXN][MAXN];
int sum;//当前龙单词长度
int ans;//最长的长度
int n;//
string dragon;
//用第 i 个单词开始接龙
void dfs(int i) {
//标志位,如果发生遍历说明数据构造没有结束
bool flag = true;
//遍历
for (int j=0; j0) {
used[j]++;
sum += (word[j].length()-relation[i][j]);
dragon += word[j].substr(relation[i][j], word[j].length()-relation[i][j]);
dfs(j);
sum -= (word[j].length()-relation[i][j]);
used[j]--;
dragon = dragon.substr(0, dragon.length()-(word[j].length()-relation[i][j]));
flag = false;
}
}
if (true == flag) {
//cout << dragon << " " << sum << endl;
ans = max(ans, sum);
}
}
int main() {
cin >> n;
for (int i=0; i>word[i];
}
char ch;
cin >> ch;
//找龙头和构建重复字符表
vector head;
for (int i=0; i::iterator it;
for (it=head.begin(); it
P.S.
写题解报告太tmd累了,这个报告前前后后写了 4+ 小时,不停的修改算法思路部分。题解报道中题解,也就是题目分析是最重要的,代码反而可以不看。