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

99 阅读5分钟

问题描述

小U拥有一个由 01 组成的字符串,她可以进行最多k次操作,每次操作可以交换相邻的两个字符。目标是通过这些操作,使得最终得到的字符串字典序最小。

例如,小U当前有一个字符串01010,她最多可以进行2次相邻字符交换操作。通过这些操作,她可以将字符串调整为00101,这是可以通过不超过2次操作得到的字典序最小的字符串。

现在,小U想知道,经过最多k次操作后,能够得到的字典序最小的字符串是什么。

测试样例

  • 样例1:
    输入:n=5,k=2,s="01010" 
    输出:'00101'
  • 样例2:
    输入:n=7,k=3,s="1101001"
    输出:'0110101'
  • 样例3:
    输入:n=4,k=1,s="1001"
    输出:'0101'

题目解析

小U需要通过最多k次相邻字符的交换,将一个由 01 组成的字符串调整为字典序最小的字符串。字典序最小的字符串,可以理解为尽可能让 0 往前移。

题意要点

  1. 每次操作只能交换相邻的两个字符。

  2. 操作的次数不能超过 k 次。

  3. 目标是返回调整后的字典序最小的字符串。

解题思路

为了实现字典序最小化,我们需要将尽可能多的 0 移到靠前的位置,同时保持最多执行 k 次操作的限制。基本思路如下:

  1. 遍历字符串: 从左到右扫描字符串,逐步确定当前字符是否需要用剩余的操作次数 k 来交换更小的字符到当前 位置。

  2. 局部最优选择: 对于每个位置 i ,在 [i,i+k] 范围内选择最小的字符,将其移动到 i 位置。

    • 如果最小字符的索引是 minIndex ,移动该字符需要的操作次数是 minIndex - i

    • 将该字符前的字符右移,同时减少操作次数 k

  3. 终止条件: 如果 k 次操作已经用完或者字符串扫描结束,直接返回结果。

核心代码详解

代码结构

  1. 遍历字符数组: 外层循环 for (int i=0;i<n && k>0; i++) 控制从左到右扫描字符串,且在操作次数耗尽时终止。

  2. 找到最小字符: 在 [i,i+k] 范围内找到最小的字符索引 minIndex

  3. 交换字符: 如果找到的最小字符不在当前位置,则需要移动它到当前 i

  4. 更新状态:每次移动后,减少操作次数 k 并更新字符串。

代码详细解析

public static String solution(int n, int k, String s) {
    char[] arr = s.toCharArray(); // 将字符串转换为字符数组,方便操作
    
    for (int i = 0; i < n && k > 0; i++) { // 遍历每个字符,直到操作次数耗尽
        int maxIndex = Math.min(i + k, n - 1); // 当前最多可查看范围
        int minIndex = i; // 初始化最小字符的索引
        
        // 在范围 [i, maxIndex] 中寻找最小字符的索引
        for (int j = i + 1; j <= maxIndex; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j; // 更新最小字符的位置
            }
        }
        
        // 如果最小字符不在当前位置,进行字符移动
        if (minIndex != i) {
            int swapsNeeded = minIndex - i; // 需要的交换次数
            for (int j = minIndex; j > i; j--) { // 将最小字符移到当前位置
                arr[j] = arr[j - 1]; // 逐步右移
            }
            arr[i] = s.charAt(minIndex); // 将最小字符放到当前位置
            k -= swapsNeeded; // 更新剩余的操作次数
        }
    }

    return new String(arr); // 返回最终字符串
}

样例详解

样例1

  • 输入: n=5, k=2,S="01010"
  • 目标: 字典序最小

步骤:

  1. 初始字符串:01010k=2

  2. i=0,范围 [0,2] :找到最小字符 0 (索引0),无需操作。

  3. i=1,范围 [1,3] :找到最小字符 0 (索引2),需要1次操作。

    • 交换结果: 00110k=1
  4. i=2,范围 [2,4] :最小字符已在当前位置,无需操作。

  5. 最终字符串:00101

输出:"00101"

样例2

  • 输入: n=7, k=3,S="1101001"
  • 目标: 字典序最小

步骤:

  1. 初始字符串:1101001k=3

  2. i=0,范围 [0,3] :找到最小字符 0 (索引0),需要2次操作。

    • 交换结果:1011001k=1
  3. i=1,范围 [1,2] :找到最小字符 0 (索引2),需要1次操作。

    • 交换结果: 0111001k=0
  4. i=2,操作次数用尽,停止。

  5. 最终字符串:0110101

输出:"0110101"

样例3

  • 输入: n=4, k=1,S="1001"
  • 目标: 字典序最小

步骤:

  1. 初始字符串:1001k=1

  2. i=0,范围 [0,1] :找到最小字符 0 (索引0),需要1次操作。

    • 交换结果:0101k=0
  3. i=1,操作次数用尽,停止。

  4. 最终字符串:0101

输出:"0101"

总结

这道题的关键在于:

  1. 范围限制: 每次只能在 k 次交换范围内选择最优解。

  2. 局部最优: 在每个范围 [i,i+k]中选择最小字符并移动。

  3. 复杂度: 假设字符串长度为 n ,每次查找最小值的复杂度为 O(k) ,总时间复杂度为 O(nk)