问题描述
小S有一个由字符 'U' 和 'C' 组成的字符串 SS,并希望在编辑距离不超过给定值 mm 的条件下,尽可能多地在字符串中找到 "UCC" 子串。
编辑距离定义为将字符串 SS 转化为其他字符串时所需的最少编辑操作次数。允许的每次编辑操作是插入、删除或替换单个字符。你需要计算在给定的编辑距离限制 mm 下,能够包含最多 "UCC" 子串的字符串可能包含多少个这样的子串。
例如,对于字符串"UCUUCCCCC"和编辑距离限制m = 3,可以通过编辑字符串生成最多包含3个"UCC"子串的序列。
测试样例
样例1:
输入:
m = 3,s = "UCUUCCCCC"
输出:3
样例2:
输入:
m = 6,s = "U"
输出:2
样例3:
输入:
m = 2,s = "UCCUUU"
输出:2
解释
样例1:可以将字符串修改为 "UCCUCCUCC"(2 次替换操作,不超过给定值 m = 3),包含 3 个 "UCC" 子串。
样例2:后面插入 5 个字符 "CCUCC"(5 次插入操作,不超过给定值 m = 6),可以将字符串修改为 "UCCUCC",包含 2 个 "UCC" 子串。
样例3:替换最后 2 个字符,可以将字符串修改为 "UCCUCC",包含 2 个 "UCC" 子串。
下面为题目分析与解答
问题分析
本题的目标是通过最多 k 次相邻字符交换操作,使得字符串 s 的字典序最小。以下是问题的关键点:
- 交换操作的限制:每次只能交换相邻字符,最多可以执行 k 次。
- 字典序:字符串的字典序越小,意味着前面的字符越小(例如 '0' 比 '1' 小)。
- 贪心思想:从左到右尝试将较小的字符移到前面,但需要注意交换的次数不能超过 k。
解题思路
我们使用贪心策略解决该问题:
- 遍历字符串 s 的每个字符。
- 每次尝试将当前字符尽量往左移动,使得字典序更小。
- 在移动过程中消耗交换次数 k,如果 k 不足以完成移动,则只能移动部分。
- 如果 k 用完,直接输出结果。
具体步骤
-
选择最小字符:
- 每次选择当前位置开始的某个最小字符,尽量将它移动到当前可移动的最左侧。
-
消耗交换次数:
- 将字符从索引 j 移到 i 需要 j - i 次交换,如果 k 不足,则移动部分,最终 kkk 减少。
-
贪心判断:
- 保证当前能够构造的前缀是字典序最小的。
代码实现
def solution(n: int, k: int, s: str) -> str:
s = list(s) # 转为列表以便操作
for i in range(n):
if k <= 0:
break # 如果没有剩余操作,直接退出
# 找到当前可移动范围内的最小字符
min_pos = i
for j in range(i + 1, min(i + k + 1, n)):
if s[j] < s[min_pos]:
min_pos = j
# 如果最小字符就在当前位置,无需操作
if min_pos == i:
continue
# 将最小字符移动到当前位置
for j in range(min_pos, i, -1):
s[j], s[j - 1] = s[j - 1], s[j]
# 更新剩余的交换次数
k -= (min_pos - i)
return ''.join(s)
# 测试用例
if __name__ == "__main__":
print(solution(5, 2, "01010")) # 输出: '00101'
print(solution(7, 3, "1101001")) # 输出: '0110101'
print(solution(4, 1, "1001")) # 输出: '0101'
代码详细分析
核心部分
-
for j in range(i + 1, min(i + k + 1, n)):- 找到从当前索引 i 开始,能通过 k 次交换到达的范围内最小字符。
- 如果当前位置已经是最小字符,直接跳过。
-
for j in range(min_pos, i, -1):- 将找到的最小字符逐步向左移动,通过逐次交换调整顺序。
- 每次移动会消耗 1 次交换。
-
k -= (min_pos - i):- 减去移动最小字符消耗的交换次数。
-
提前退出:
- 当 k≤0 时,直接退出循环,因为已经没有足够的交换次数。
时间复杂度
- 外层循环遍历字符串:O(n)。
- 内层查找范围内最小字符:每次最多需要 O(k)。
- 移动字符需要 O(k)。
- 总复杂度:O(n⋅k)。
空间复杂度
- 因为操作的是字符串的列表,没有使用额外空间:O(n)。
测试用例解析
测试用例 1
输入:n=5,k=2,s="01010"
- 初始字符串:
01010 - 第 1 步:在索引 i=0 开始,可在范围 [0, 2] 内找到最小字符 '0',无需移动。
- 第 2 步:在索引 i=1 开始,可在范围 [1, 3] 内找到最小字符 '0',移动 1 步。
- 结果:
00101。
测试用例 2
输入:n=7,k=3,s="1101001"
- 初始字符串:
1101001 - 第 1 步:在索引 i=0 开始,找到范围 [0, 3] 内最小字符 '0',移动到索引 0,消耗 2 次操作。
- 第 2 步:在索引 i=1 开始,找到范围 [1, 2] 内最小字符 '1',无需移动。
- 第 3 步:在索引 i=2 开始,找到范围 [2, 3] 内最小字符 '0',移动到索引 2,消耗 1 次操作。
- 结果:
0110101。
测试用例 3
输入:n=4,k=1,s="1001"
- 初始字符串:
1001 - 第 1 步:在索引 i=0 开始,找到范围 [0, 1] 内最小字符 '0',移动到索引 0,消耗 1 次操作。
- 结果:
0101。
总结
- 本算法利用贪心策略,逐步寻找最优解。
- 对于每个字符,尽量选择当前范围内的最小字符,移动到目标位置。
- 时间复杂度为 O(n⋅k),适用于一般的输入规模。