1 条题解
-
0
给定 和 ,要求构造一个字符串 ,使得 所有由前 个小写字母组成的、长度为 的字符串 都是 的子序列。
我们要找 长度最短 的 ,如果有多个,任意输出一个。
关键观察
- 所有可能字符串的个数是 。
- 每个长度为 的字符串都是 的子序列,意味着:
- 对任意序列 (),
- 存在 的下标 ,使得 。
最小长度下界
- 子序列不能重复使用同一个字符在同一个位置(不同位置可以相同字符)。
- 最坏情况下,要覆盖所有 种序列, 的长度至少是 ?
不对,因为子序列允许跳字符,不是相邻排列。
实际上,这里可以这样想:
我们要能选出任意 个位置,每个位置可以是前 个字母中的任意一个。
这等价于:对于每个字母 ,在 中要有足够多的 ,使得在取 个位置时,每个位置的选择是独立的。
已知结论(类似 de Bruijn 序列的子序列版): 最短长度是 ?
不,那是相邻排列(de Bruijn 序列)的长度。这里是子序列,要求更弱,所以可以更短。实际上,经典解法是:
构造方法:
把前 个字母重复 遍,按顺序排列:
abc...k abc...k ...重复 次?
这样长度 ,但还不够短。
已知最短构造(来自题解)
对于 :
abc...k(前 个字母),长度 。
确实最短,因为要包含每个长度为 1 的字符串(每个字母一次)。对于 :
abc...k abc...k(重复两次)长度 ,但可以更短。例子中 输出
baab长度 4,比 一样。
输出abcbac长度 6,比 一样。其实这个构造就是:
前 个字母的排列 + 前 个字母的排列,但顺序不同,使得每个位置可以独立选。
一般构造法
设字母为 。
构造字符串为:
重复 次,长度 。
但这样够短吗?检查 :
长度 6,确实包含所有 9 个长度为 2 的子序列吗?
可以:
aa: 位置 1 和 4(1 和 1)
ab: 位置 1 和 2(1 和 2)
ac: 位置 1 和 3(1 和 3)
ba: 位置 2 和 4(2 和 1)
bb: 位置 2 和 5(2 和 2)
bc: 位置 2 和 3?不对,位置 2 是 2,位置 3 是 3,那是 b c 没问题。
等等,位置 2 和 3 是2,3,就是bc,对。
ca: 位置 3 和 4(3 和 1)
cb: 位置 3 和 5(3 和 2)
cc: 位置 3 和 6(3 和 3)
可行。检查 用这个构造:
长度 4,包含 aa,ab,ba,bb?
aa: 位置 1 和 3(1 和 1)
ab: 位置 1 和 2(1 和 2)
ba: 位置 2 和 3(2 和 1)
bb: 位置 2 和 4(2 和 2)
可行。但题解中 输出
baab,也是 4,只是顺序不同。所以这个构造确实是最优的:
长度为 的字符串,由前 个字母重复 次连接而成,可以覆盖所有 种子序列。
为什么 是最短的?
反证:
如果长度 ,那么某个字母的出现次数少于 次。
考虑全部由该字母组成的字符串 ( 个 ),它不可能作为子序列,因为 在 中不足 次。
所以 。因此 最小长度就是 ,构造就是重复 次
1..k。
最终算法
对于每个测试用例 :
- 令 。
- 对于 到 :
- 对于 到 :
- 加上第 个小写字母(
'a'+j-1)。
- 加上第 个小写字母(
- 对于 到 :
- 输出 。
复杂度
每个测试用例 ,总 ,非常小。
代码(伪代码)
for _ in range(t): n, k = map(int, input().split()) s = "" for i in range(n): for j in range(k): s += chr(ord('a') + j) print(s)
示例验证:
- :
ab✅ - :
aa✅ - :
abab长度 4,比baab一样长 ✅ - :
abcabc长度 6 ✅
这样我们就得到了题目要求的最短字符串构造。
- 1
信息
- ID
- 6417
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者