学习方法与心得-难19-字典序最小的01字符串题目解析 | 豆包MarsCode AI 刷题

161 阅读4分钟

字典序最小的01字符串题目解析

引言

在字符串算法中,调整字符串以满足某种最优化条件是一个常见的问题。本次我们讨论的是如何在限定次数的相邻字符交换操作下,将由01组成的二进制字符串调整为字典序最小的形式。这不仅考验对字符串的理解,也需要巧妙地设计算法来最小化交换次数。

问题描述

给定一个仅包含字符01的长度为n的字符串s,你可以进行最多k次相邻字符交换操作。每次操作可以交换字符串中相邻的两个字符。你的目标是通过这些操作,使得最终得到的字符串的字典序最小。

示例:

  • 输入:s = "01010", k = 2
  • 输出:"00101"

通过最多2次交换操作,可以将字符串调整为00101,这是在限定操作次数内字典序最小的字符串。

解题思路

要使得字符串的字典序最小,我们需要尽可能地将字符0移动到前面。然而,每次只能交换相邻的字符,并且交换次数受到限制。因此,我们需要在有限的交换次数内,将尽可能多的0移动到前面。

具体思路如下:

  1. 遍历字符串:从左到右遍历字符串中的每个字符。
  2. 遇到字符0:尝试将其向前移动,直到无法移动或者达到交换次数限制。
  3. 更新交换次数:每次交换后,减少可用的交换次数k
  4. 终止条件:当交换次数用完(k == 0),或者遍历完字符串时,停止操作。

图解示例

以字符串"01010"k = 2为例:

初始状态:

索引01234
字符01010
  1. 第一个字符0

    • 已经在最前面,无需移动。
  2. 第二个字符1

    • 跳过,因为我们只在遇到0时尝试移动。
  3. 第三个字符0

    • 尝试向前移动:

      • 与前面的1交换,k减1。

      • 新状态:

        索引01234
        字符00110
      • 再次与前面的0(索引1)尝试交换,但前面是0,无需交换。

  4. 第四个字符1

    • 跳过。
  5. 第五个字符0

    • 尝试向前移动:

      • 与前面的1交换,k减1。

      • 新状态:

        索引01234
        字符00101
      • 由于k已减为0,无法继续交换。

最终结果为"00101"

代码详解

下面是实现上述思路的Java代码:

public class Main {
    public static String solution(int n, int k, String s) {
        char[] arr = s.toCharArray();

        for (int i = 0; i < n && k > 0; i++) {
            if (arr[i] == '0') {
                int j = i;
                // 尝试将当前的'0'向前移动
                while (j > 0 && arr[j - 1] == '1' && k > 0) {
                    // 交换当前字符与前一个字符
                    arr[j] = '1';
                    arr[j - 1] = '0';
                    j--;
                    k--;
                }
            }
        }

        return new String(arr);
    }

    public static void main(String[] args) {
        System.out.println(solution(5, 2, "01010")); // 输出: 00101
        System.out.println(solution(7, 3, "1101001")); // 输出: 0110101
        System.out.println(solution(4, 1, "1001")); // 输出: 0101
    }
}

代码解释

方法 solution
  • 参数说明

    • int n:字符串的长度。
    • int k:最多可以进行的交换次数。
    • String s:初始的二进制字符串。
  • 主要步骤

    1. 将字符串转换为字符数组:便于修改字符。

      char[] arr = s.toCharArray();
      
    2. 遍历字符数组

      for (int i = 0; i < n && k > 0; i++) {
      
      • 遍历条件为i < nk > 0,如果交换次数耗尽,直接退出循环。
    3. 遇到字符0时的处理

      if (arr[i] == '0') {
      
      • 当当前字符为0时,尝试向前移动。
    4. 内层循环:将0向前移动

      int j = i;
      while (j > 0 && arr[j - 1] == '1' && k > 0) {
          arr[j] = '1';
          arr[j - 1] = '0';
          j--;
          k--;
      }
      
      • 条件

        • j > 0:防止越界,确保有前一个字符。
        • arr[j - 1] == '1':只有前一个字符是1,才需要交换。
        • k > 0:确保还有剩余的交换次数。
      • 操作

        • 交换当前位置的0与前一个位置的1
        • 指针j前移一位,继续尝试向前移动。
        • 交换次数k减1。
    5. 返回结果

      return new String(arr);
      
      • 将字符数组转换回字符串,作为最终结果。

结论

通过上述算法,我们在限定的交换次数内,将字符串调整为字典序最小的形式。该算法的时间复杂度为O(n^2),最坏情况下需要对每个0尝试向前移动,但由于交换次数有限,实际运行效率较高。

这种贪心的策略充分利用了每一次交换,使得0尽可能地向前移动,达到了全局最优解。


作者:AWM