字典序最小的二进制字符串的求解
问题描述
给定一个长度为 n 的仅由字符 '0' 和 '1' 组成的字符串 s,你可以进行最多 k 次操作。每次操作可以交换相邻的两个字符。目标是通过这些操作,使得最终得到的字符串字典序最小。
举例说明:
- 输入:
n = 5,k = 2,s = "01010" - 输出:
"00101"
在这个例子中,通过最多 2 次相邻字符交换,可以将字符串调整为 "00101",这是可以得到的字典序最小的字符串。
问题分析
要在有限的操作次数内使二进制字符串的字典序最小,需要尽可能将 '0' 移动到字符串的左侧。由于每次只能交换相邻字符,每次将 '0' 向左移动一位需要一次操作。因此,我们需要在操作次数限制内,尽可能多地将 '0' 移动到左边。
关键点:
-
交换相邻字符的代价:将位于位置
i的'0'移动到位置j(j < i)需要i - j次交换。 -
贪心策略:每次尽可能将当前的
'0'移动到最左边,直到操作次数耗尽。 -
操作限制:由于操作次数有限,我们需要在不超过
k次操作的情况下,安排'0'的移动。
解题思路
基于以上分析,我们可以采用以下贪心算法:
-
初始化:
- 创建一个结果数组
res,用于构建最终的字符串。 - 设置变量
write_pos,表示下一个可以放置'0'的位置。 - 记录剩余的操作次数
k_remaining。
- 创建一个结果数组
-
遍历字符串:
-
对于每个字符,如果是
'0':-
计算该
'0'可以向左移动的最大步数:moves_possible = min(k_remaining, index - write_pos)index是当前'0'在原字符串中的位置。write_pos是当前可以放置'0'的最左位置。
-
更新
'0'的新位置:new_pos = index - moves_possible -
将
'0'放置在res的new_pos位置。 -
更新剩余的操作次数:
k_remaining -= moves_possible -
更新下一个
'0'可以放置的位置:write_pos += 1
-
-
如果是
'1',暂时跳过,稍后再填充。
-
-
填充
'1':- 在遍历完成后,将
res中未被填充的位置用'1'补上。
- 在遍历完成后,将
-
生成结果字符串:
- 将
res转换为字符串并返回。
- 将
算法实现
def solution(n: int, k: int, s: str) -> str:
res = [None] * n # 结果数组
write_pos = 0 # 下一个可放置 '0' 的位置
k_remaining = k # 剩余操作次数
for index in range(n):
if s[index] == '0':
# 计算当前 '0' 可以移动的最大步数
moves_possible = min(k_remaining, index - write_pos)
new_pos = index - moves_possible
res[new_pos] = '0'
k_remaining -= moves_possible
write_pos += 1 # 下一个 '0' 的放置位置右移
# 对于 '1',暂不处理
# 填充剩余位置为 '1'
for i in range(n):
if res[i] is None:
res[i] = '1'
return ''.join(res)
if __name__ == '__main__':
print(solution(5, 2, "01010") == '00101') # 输出 True
print(solution(7, 3, "1101001") == '0110101') # 输出 True
print(solution(4, 1, "1001") == '0101') # 输出 True
复杂度分析
-
时间复杂度:O(n),其中 n 是字符串的长度。遍历字符串和结果数组各一次。
-
空间复杂度:O(n),需要额外的数组
res存储结果。
正确性证明
-
贪心选择性质:
在每一步,我们都将当前的
'0'尽可能向左移动,这样可以保证在当前情况下字典序最小。 -
全局最优性:
由于每个
'0'都尽可能向左移动,在操作次数允许的范围内,没有其他策略能比这个策略得到字典序更小的字符串。 -
操作次数限制:
通过
k_remaining确保总的交换次数不超过k。
总结
这道题要求在有限次数的相邻交换操作下,得到字典序最小的字符串。通过贪心算法,我们每次将 '0' 尽可能向左移动,直到操作次数耗尽。该算法充分利用了交换相邻字符的特点,操作简单,时间效率高,适用于大规模数据。
扩展思考
-
如果字符集扩大:
如果字符串中包含多个不同的字符,比如小写字母,那么需要采用更复杂的算法,如优先队列或树状数组。
-
如果操作不限于相邻交换:
如果允许交换任意位置的字符,那么可以直接对字符串排序。
-
优化空间复杂度:
可以尝试在原字符串上进行修改,减少空间开销。