视频加载失败

安吉D9-T3

944 字
5 分钟
安吉D9-T3
原题呈现

P1688 新单词接龙问题#

题目描述#

给定一个包含 nn 个单词的字典,定义字典序为单词在这本字典的顺序.从中选择若干个单词,按字典序进行单词接龙,使得接龙的长度最大.

新单词接龙的规则:

  1. 单词变换:单词 wiw_i 添加一个字母,删除一个字母,或修改一个字母可以得到单词 wi+1w_{i+1}
  2. 字典序接龙:w1,w2,,wnw_1,w_2,\cdots,w_n,满足字典序.

输入格式#

第一行一个整数 nn1n25,0001 \le n \le 25,000),表示字典中单词的总数.接下来 nn 行,按字典序输入 nn 个单词,每行一个字符串,表示单词,单词仅由小写字母组成,长度在 111616 以内.

输出格式#

输出一行一个整数,表示能获得的单词接龙的最大长度.

输入输出样例 #1#

输入 #1#

9
cat
dig
dog
fig
fin
fine
fog
log
wine

输出 #1#

5

说明/提示#

样例解释#

长度为 55 的单词接龙为:digfigfinfinewine\texttt{dig}\to \texttt{fig}\to \texttt{fin}\to \texttt{fine}\to\texttt{wine}

此题动态规划.类似最长上升子序列问题,定义 f[n] 表示以 nn 结尾的字符串最长的接龙长度.

观察到字符串的长度很小,远小于 nn.因此,对于此题,我们在转移时不选择枚举之前的所有字符串,而是选择枚举所有能够通过一次修改转移到当前字符串的前置字符串.具体来说:

  • 枚举每一个字符串 ii,在枚举时,遍历所有可能的前置字符串(相对 ii 多一个字符、少一个字符、改一个字符),并在前面所有枚举过的给定字符串中查找,如果存在这样的前置字符串 jj,则说明 ii 可以接在 jj 后面,可以转移:将 f[i] = f[j] + 1

这样的算法枚举前置字符串的复杂度为 O(53L)O(53L),每一次比较是 O(L)O(L),总复杂度是 O(53L2n2)O(53L^2n^2) 的,.

考虑优化枚举前面的字符串,可以使用 unordered_map umum 存储前面所有遍历过的给定字符串对应的 f 值,直接在 umum 中查找,这样的时间复杂度是 O(53L2n)O(53L^2n) 的.

考虑使用哈希优化,这样判断两个字符串相等就可以在 O(1)O(1) 的复杂度内完成,时间复杂度 O(53Ln)O(53Ln)

由于此题字符串比较多,推荐使用 unsigned long long 自然溢出来实现哈希,不易冲突的同时且能够做到时间常数较小.

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 25005;
const int BASE = 129;
int m, f[N], ans;
string s[N];
unordered_map<ull, int> um;
ull h[N], p[N];
// l,r 1-based
ull H(int l, int r) {
if (l > r)
return 0;
return h[r] - h[l - 1] * p[r - l + 1];
}
signed main() {
cin >> m;
p[0] = 1;
for (int i = 1; i <= 20; i++) {
p[i] = p[i - 1] * BASE;
}
for (int q = 1; q <= m; q++) {
string s;
cin >> s;
int len = s.size();
for (int i = 1; i <= len; i++) {
h[i] = h[i - 1] * BASE + s[i - 1];
}
f[q] = 1;
// 增加一个
for (int i = 0; i <= len; i++) {
for (char c = 'a'; c <= 'z'; c++) {
ull hs =
H(1, i) * p[len - i + 1] + c * p[len - i] + H(i + 1, len);
if (um.count(hs))
f[q] = max(f[q], f[um[hs]] + 1);
}
}
// 删除一个
for (int i = 1; i <= len; i++) {
ull hs = H(1, i - 1) * p[len - i] + H(i + 1, len);
if (um.count(hs))
f[q] = max(f[q], f[um[hs]] + 1);
}
// 修改一个
for (int i = 1; i <= len; i++) {
for (char c = 'a'; c <= 'z'; c++) {
ull hs = H(1, i - 1) * p[len - i + 1] + c * p[len - i] +
H(i + 1, len);
if (um.count(hs))
f[q] = max(f[q], f[um[hs]] + 1);
}
}
ans = max(ans, f[q]);
um[H(1, len)] = q;
}
cout << ans;
return 0;
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

安吉D9-T3
https://blog.jerrylab.top/posts/problem/anji2026/D9/T3/
作者
Jerry
发布于
2026-08-09
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0