19.字典序最小的01字符串
一、问题描述
小 U 拥有一个由 0 和 1 组成的字符串,她可以进行最多 k 次操作,每次操作能够交换相邻的两个字符。目标是经过这些操作后,得到一个字典序最小的字符串。例如,给定字符串 “01010”,最多可进行 2 次相邻字符交换操作,通过合理操作可将其调整为 “00101”,此为不超过 2 次操作能得到的字典序最小的字符串。现在需要确定经过最多 k 次操作后,对于任意给定的由 0 和 1 组成的字符串,能够得到的字典序最小的字符串是什么。
二、思路解析
- 贪心策略:
- 整体思路采用贪心算法,即每次操作都选择当前状态下局部最优的决策,希望通过一系列局部最优的选择达到全局最优解(在本题中即得到字典序最小的字符串)。
- 具体到本题,每次遇到字符‘1’时,优先尝试将其后面最近的‘0’交换到当前‘1’的位置,因为这样能使‘1’尽可能往前移,从而让字符串的字典序有变小的趋势。
- 操作限制考虑:
- 在进行交换操作时,需要时刻关注剩余的可操作次数 k。只有当把某个‘0’交换到当前‘1’位置所需的交换次数小于等于 k 时,才进行该交换操作,否则就跳过,继续检查下一个字符。
三、解题步骤
- 数据预处理:
- 将输入的由 0 和 1 组成的字符串 s 转换为列表 s_list,这是因为字符串在 Python 中是不可变类型,不方便进行字符的交换操作,而列表是可变类型,便于后续的交换处理。
- 遍历字符串:
- 使用一个循环遍历字符串 s_list 中的每个字符,循环变量 i 从 0 到字符串长度 n - 1。
- 在每次循环中,首先检查剩余的操作次数 k 是否为 0,如果是则直接退出循环,因为已经没有可进行的操作了。
- 寻找可交换字符并操作:
- 当遇到字符‘1’时,从该字符的下一个位置(i + 1)开始,继续往后遍历字符串,寻找第一个字符‘0’。
- 一旦找到字符‘0’,计算将这个‘0’交换到当前‘1’位置所需的交换次数 swaps,即两个字符位置的差值(j - i)。
- 如果 swaps 小于等于剩余的操作次数 k,就进行字符的交换操作(通过 s_list [i], s_list [j] = s_list [j], s_list [i]),并更新剩余的操作次数 k(减去已经使用的交换次数 swaps),然后跳出内层寻找‘0’的循环,继续下一个字符的检查。
- 结果返回:
- 遍历完整个字符串或者操作次数 k 用完后,将处理后的列表 s_list 转换回字符串形式,作为最终结果返回。
四、代码分析
以下是结合上述解题步骤对示例代码的详细分析:
def solution(n: int, k: int, s: str) -> str:
# 将字符串转换为列表以便于交换操作
s_list = list(s)
# 遍历字符串
for i in range(n):
if k == 0:
break
if s_list[i] == '1':
# 找到第一个 '0' 的位置
for j in range(i + 1, n):
if s_list[j] == '0':
# 计算需要交换的次数
swaps = j - i
if swaps <= k:
# 进行交换
s_list[i], s_list[j] = s_list[j], s_list[i]
# 更新剩余操作次数
k -= swaps
break
# 将列表转换回字符串
return ''.join(s_list)
- 函数定义部分:
solution函数接受三个参数:n表示输入字符串的长度,k表示可以进行交换操作的最大次数,s是由 0 和 1 组成的输入字符串。函数的返回值是经过最多k次操作后字典序最小的字符串。
- 数据预处理代码分析:
s_list = list(s)这行代码将输入字符串s转换为列表s_list,为后续的交换操作做准备。
- 遍历字符串及操作代码分析:
for i in range(n)开始遍历字符串,循环变量i从 0 到n - 1。- 在每次循环中,
if k == 0:条件判断用于检查剩余操作次数是否为 0,如果是则直接退出循环,避免无效的遍历。 - 当
s_list[i] == '1':时,进入内层循环寻找可交换的‘0’。for j in range(i + 1, n):从i + 1位置开始往后遍历字符串寻找‘0’。 - 找到‘0’后,
swaps = j - i计算交换次数,if swaps <= k:判断是否满足操作次数限制,如果满足则进行交换操作s_list[i], s_list[j] = s_list[j], s_list[i],并更新k的值k -= swaps,然后跳出内层循环。
- 结果返回代码分析:
return ''.join(s_list)将处理后的列表s_list转换回字符串形式,作为最终的结果返回。
五、知识总结
- 贪心算法:
- 本题主要运用了贪心算法的思想,通过每次选择当前看起来最优的操作(将‘1’后面最近的‘0’交换过来),来尝试达到最终的全局最优解(得到字典序最小的字符串)。但需要注意的是,贪心算法并不一定能保证在所有情况下都能得到真正的全局最优解,不过在本题的特定情境下是有效的。
- 字符串与列表的转换:
- 在 Python 中,字符串是不可变类型,这意味着一旦创建就不能直接修改其字符内容。而列表是可变类型,可以方便地进行元素的修改、添加、删除等操作。在本题中,为了便于进行字符的交换操作,先将字符串转换为列表,处理完后再转换回字符串。这种转换操作在处理字符串相关问题且需要对字符进行动态操作时经常会用到。
- 循环与条件判断的运用:
- 代码中大量运用了循环(如
for循环)来遍历字符串,以及条件判断(如if语句)来根据不同的情况决定是否进行某些操作(如是否进行字符交换、是否退出循环等)。熟练掌握循环和条件判断的使用是编写程序解决各种问题的基础,它们可以帮助我们按照特定的逻辑流程来处理数据和执行操作。
- 代码中大量运用了循环(如