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

121 阅读4分钟

问题背景与分析

问题描述

小U拥有一个由0和1组成的字符串,她可以进行最多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'

问题分析

  1. 目标明确:我们需要通过最多 k 次相邻字符交换操作,使字符串的字典序最小。
  2. 操作限制:每次只能交换相邻的两个字符。
  3. 字典序最小:字典序是指字符串在字典中的顺序,例如 "00101" 的字典序小于 "01001"

解决方案设计

思路

  1. 优先移动0:由于0在字典序中排在1之前,因此优先将0尽可能地向前移动,可以使字符串的字典序最小。
  2. 贪心算法:每次找到最靠前的1,并在其后面找到最近的0,然后将这个0移动到1的前面,直到用完所有的操作次数 k 或者无法再进行有效的移动。

具体步骤

  1. 初始化:将输入字符串 s 转换为字符数组 chars,以便于操作。
  2. 外层循环:遍历字符数组 chars,直到处理完所有字符或 k 次操作用完。
  3. 内层循环:如果当前字符是 '1',则在当前位置 i 之后寻找第一个 '0'
  4. 计算交换次数:计算从 '0' 的位置 j 到当前位置 i 的距离 swapCount
  5. 执行交换:如果 swapCount 小于等于剩余的 k 次操作次数,则进行交换操作,将 '0' 移动到当前位置 i,并将中间的字符依次向后移动。
  6. 更新操作次数:更新 k 的值,减去已经使用的交换次数。
  7. 返回结果:最终将字符数组 chars 转换回字符串并返回。

代码实现


public String solution(int n, int k, String s) {
    char[] chars = s.toCharArray();

    for (int i = 0; i < n && k > 0; i++) {
        if (chars[i] == '0') {
            continue;
        }
        for (int j = i + 1; j < n && k > 0; j++) {
            if (chars[j] == '0') {
                int swapCount = j - i;
                if (swapCount <= k) {
                    char temp = chars[j];
                    for (int m = j; m > i; m--) {
                        chars[m] = chars[m - 1];
                    }
                    chars[i] = temp;
                    k -= swapCount;
                }
                break;
            }
        }
    }

    return new String(chars);
}

代码解析

  1. 初始化

    • 将输入字符串 s 转换为字符数组 chars,以便于操作。
  2. 外层循环

    • 遍历字符数组 chars,直到处理完所有字符或 k 次操作用完。
    • 如果当前字符是 '0',则跳过本次循环,继续检查下一个字符。
  3. 内层循环

    • 如果当前字符是 '1',则在当前位置 i 之后寻找第一个 '0'

    • 计算从 '0' 的位置 j 到当前位置 i 的距离 swapCount

    • 如果 swapCount 小于等于剩余的 k 次操作次数,则进行交换操作:

      • 将 '0' 移动到当前位置 i,并将中间的字符依次向后移动。
      • 更新 k 的值,减去已经使用的交换次数。
    • 交换完成后,跳出内层循环,继续处理下一个字符。

  4. 返回结果

    • 最终将字符数组 chars 转换回字符串并返回。

个人思考与分析

贪心算法的有效性

在这个问题中,贪心算法是非常有效的。因为每次将 '0' 尽可能地向前移动,可以确保每一步都使字符串的字典序变小。这种局部最优的选择最终会导全局最优解。

时间复杂度分析

  • 外层循环:遍历字符串的每个字符,时间复杂度为 O(n)
  • 内层循环:在最坏情况下,每次都需要遍历剩余的所有字符,时间复杂度为 O(n^2)

总体时间复杂度为 O(n^2),对于较小的字符串长度是可以接受的。如果字符串长度非常大,可以考虑使用更高效的数据结构(如双端队列)来优化交换操作。

空间复杂度分析

  • 字符数组:将字符串转换为字符数组,空间复杂度为 O(n)
  • 其他变量:常数级别的额外空间,空间复杂度为 O(1)

总体空间复杂度为 O(n)