题目解析:字典序最小的01字符串 | 豆包MarsCode AI 刷题

111 阅读5分钟

题目解析

在这道题目中,我们需要对一个由 01 组成的字符串进行修改,目标是通过至多 k 次相邻字符交换操作,得到字典序最小的字符串。字典序最小意味着字符串中尽可能多的 0 位于前面,1 位于后面。通过相邻字符交换,我们可以逐步调整字符串的顺序,使其字典序最小。

示例:

  1. 输入:n = 5, k = 2, s = "01010"
    输出:"00101"
    解释:在最多 2 次交换操作后,字典序最小的字符串是 "00101"
  2. 输入:n = 7, k = 3, s = "1101001"
    输出:"0110101"
    解释:通过 3 次交换操作后,字符串变为 "0110101",这是字典序最小的结果。

思路设计

本题的核心任务是通过相邻字符交换,逐步将 0 移到字符串的前面,从而使字符串的字典序变得最小。为了在有限的交换次数内完成这一任务,我们采用了递归的方法,在每次递归中都尽量将最左边的 0 推到当前索引位置。

1. 问题的本质

要使二进制字符串的字典序最小,应该尽量将 0 放到字符串的前面,1 放到字符串的后面。字典序最小的字符串应当包含尽可能多的 0 字符排在前面。因此,在每次操作中,我们通过交换相邻字符,逐步将 0 移动到最前面。

2. 递归的基本情况

递归的基本情况用来控制递归的停止条件,避免无限循环。具体条件如下:

  • 交换次数为零:如果剩余的交换次数 k 为 0,表示无法再进行交换,递归停止。
  • 遍历完整个字符串:如果处理的索引 start 达到或超过字符串的长度,说明所有字符都已经处理完,递归停止。

这些条件保证了递归能在合理的情况下结束,防止程序进入无穷递归。

3. 寻找可交换的0

每一轮递归中,我们的目标是找到当前未处理部分中最左边的 0,并尽可能将其交换到当前处理位置 start。具体步骤如下:

  • 从当前索引 start 开始,遍历字符串中的字符,找到最左边的 0
  • 计算将该 0 移到 start 位置所需的交换次数。即,找到的 0start 之间的距离。
  • 如果剩余的交换次数 k 足够,则执行交换操作,并将剩余的交换次数 k 减去相应的次数。

4. 交换操作

交换操作是这道题的核心。每当我们找到一个 0,就通过逐步交换相邻字符将其移到 start 位置。具体步骤如下:

  • 将找到的 0 逐步交换到 start 位置。每次交换相邻的字符,直到 0 到达目标位置。
  • 每完成一次交换,交换次数 k 就减去相应的次数。如果交换次数用完,递归提前终止,返回当前字符串。

5. 递归的后续处理

当交换完成后,我们需要递归处理下一个位置,继续寻找并交换 0。具体步骤:

  • 递归调用处理下一个位置 start + 1,继续查找最左边的 0 并将其交换到当前处理的位置。
  • 如果交换次数 k 用尽,或整个字符串处理完毕,递归停止。

6. 最终结果

当递归结束时,字符串已经经过了最多 k 次交换,得到字典序最小的字符串。由于递归的每一步操作都尽量将 0 移到前面,保证了每次操作都是局部最优的,因此最终得到的字符串就是字典序最小的结果。

7. 优化交换策略

通过递归的方式,我们逐步查找最左边的 0 并将其推到当前 start 位置。这种局部最优策略使得每次交换都能最大化其效果,从而保证在有限的交换次数内,字符串的字典序变得尽可能小。

递归过程详解

在递归过程中,以下是每一步的具体操作:

  1. 寻找最左边的 0:从当前索引 start 开始,遍历字符串,找到最左边的 0。我们每找到一个 0,就尽量将其通过交换移到 start 索引位置。对于每个 0,我们需要计算它到当前 start 的距离,并判断是否能够通过剩余的交换次数将它移动到目标位置。
  2. 交换操作:找到一个 0 后,将其逐步交换到 start 位置。每次交换相邻字符后,减少 k。如果 k 用尽,递归提前终止,返回当前字符串。
  3. 递归调用:当当前的 0 被交换到目标位置后,递归处理下一个位置,即 start + 1。如果 k 用尽,或已经遍历完整个字符串,则递归终止。

通过这种方式,我们能够确保每次操作尽量减少后续的交换次数,最大化字典序的优化。

代码展示

代码小抄.png