问题描述
小U拥有一个由0和1组成的字符串,她可以进行最多k次操作,每次操作可以交换相邻的两个字符。目标是通过这些操作,使最终的字符串字典序最小。例如:
- 输入字符串为
01010,允许最多k=2次操作,字典序最小化后结果为00101。
解题思路
核心思想
使用贪心法,逐步在当前位置找到可以用有限步数内交换过来的最小字符,将其移动到当前位置,从而使字符串字典序最小:
-
逐位处理:
- 遍历字符串,从左到右依次处理每个字符。
- 对于当前字符,在剩余最多
k步范围内找到字典序最小的字符。
-
交换操作:
- 如果找到的字符不在当前位,则通过相邻交换将其移动到当前位置,消耗相应的步数。
- 更新剩余步数
k。
-
终止条件:
- 如果步数
k用尽,则直接返回当前字符串。
- 如果步数
详细步骤
-
将字符串转换为列表(便于操作)。
-
从左到右逐位处理:
- 在当前位置
i和可操作范围内([i+1, min(i+k+1, n)]),找到字典序最小的字符及其位置min_pos。 - 如果
min_pos与当前位不同,通过交换将min_pos的字符移动到i位置,并消耗相应步数k -= (min_pos - i)。
- 在当前位置
-
如果
k消耗完毕,或者所有字符处理完毕,结束操作。
代码实现
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:
k -= (min_pos - i) # 消耗步数
min_char = s[min_pos] # 记录最小字符
# 将最小字符移动到当前位置
for j in range(min_pos, i, -1):
s[j] = s[j - 1]
s[i] = min_char
return ''.join(s) # 转换为字符串返回
复杂度分析
-
时间复杂度:O(n * k)
- 外层循环遍历字符串,复杂度为 O(n)。
- 内层循环寻找最小字符的范围最多为
k,整体复杂度 O(n * k)。
-
空间复杂度:O(n)
- 使用了一个列表存储字符串,空间复杂度为 O(n)。
测试用例
示例 1
-
输入:
n = 5, k = 2, s = "01010" -
输出:
"00101" -
解释:
- 初始:
01010 - 第1步:交换
1和0,得00110(消耗1步)。 - 第2步:交换
1和0,得00101(消耗1步)。
- 初始:
示例 2
-
输入:
n = 7, k = 3, s = "1101001" -
输出:
"0110101" -
解释:
- 初始:
1101001 - 第1步:交换
0和1,得1011001(消耗1步)。 - 第2步:交换
0和1,得0111001(消耗2步)。
- 初始:
示例 3
-
输入:
n = 4, k = 1, s = "1001" -
输出:
"0101" -
解释:
- 初始:
1001 - 第1步:交换
0和1,得0101(消耗1步)。
- 初始:
总结
-
贪心策略:在每一步中,尽量找到可以用最小步数移动到当前位置的字典序最小字符。
-
灵活调整范围:通过
min(i+k+1, n)限制范围,保证不会超出剩余步数。 -
优点:
- 简洁高效,充分利用有限步数。
- 易于扩展到其他类似问题。