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

105 阅读5分钟

image.png

题目分析

在这道题中,需要通过最多 k 次交换相邻字符的操作来将一个由 0 和 1 构成的字符串转换为字典序最小的字符串。为了完成这个任务,我们的目标需要将字符串中的 0 尽可能地提前,1 留在后面。所以需要利用每次交换的机会,去最小化最终的字符串的字典序。

字典序的定义

字典序是按字符的顺序排列的方式,对于二进制字符串来说,0 的字典序小于 1。所以,我们的目标是尽可能将所有的 0 移到字符串的前面,1 留在后面。具体来说,字符串的字典序最小的排列就是把所有的 0 按照它们在原字符串中的顺序排列在前,1 按顺序排列在后。

解题思路

  1. 交换操作限
  • 每次操作只能交换相邻的两个字符。
  • 最多能进行 k 次操作。
  1. 贪心策略
  • 从字符串的左端开始遍历,每遇到一个 0,尝试将其交换到前面去,尽量减少操作次数。
  • 由于每次交换只能发生在相邻字符之间,所以我们需要贪心地选择最近的 0 进行交换,以此来让 0 尽量靠近字符串的最左边。
  1. 具体步骤
  • 逐步遍历字符串,对于每个位置 i,如果当前位置是 0,则尝试将其向前交换,直到交换次数用完或无法继续交换。
  • 对于每个 0,我们从当前位置向左扫描,如果左边的 1 离当前位置较近且有足够的交换次数,则进行交换,直到 k 次交换用完或不能再交换。
  1. 效率分析
  • 在最坏情况下,我们需要遍历整个字符串,并且对于每个 0 可能需要进行若干次交换。因此,时间复杂度是 O(n * k),其中 n 是字符串的长度,k 是交换次数的上限。

代码实现

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') {
                // 尝试把这个0往前移动
                for (int j = i - 1; j >= 0 && arr[j] == '1' && k > 0; j--) {
                    // 交换 arr[j] 和 arr[j+1]
                    arr[j] = '0';
                    arr[j + 1] = '1';
                    k--; // 每做一次交换,减少剩余操作次数
                }
            }
        }
        
        // 将字符数组转回字符串并返回
        return new String(arr);
    }

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

代码解析

  1. 字符串转换为字符数组
  • 因为字符串是不可变的,在需要修改字符串内容时,首先将字符串转化为字符数组 arr。这样可以在字符数组上直接进行操作。
  1. 遍历字符串
  • 从字符串的左侧开始遍历。对于每个字符,如果当前字符是 '0',则尝试将其向前交换。
  1. 交换操作
  • 如果遇到一个 '0',从其左边开始扫描,找到最近的 '1' 并进行交换。每交换一次,就将 k 减 1,表示已经使用了一个交换操作。
  1. 输出结果
  • 经过遍历和交换操作后,得到最终的字符数组,然后将其转回字符串并返回。

思路分析

这个问题本质上是一个贪心问题:我们希望通过有限的交换操作,尽可能地将 0 移动到字符串的前面,从而实现字典序最小。

贪心策略的合理性

在这个问题中,贪心策略的核心思想是尽可能用每次交换机会将一个 0 移动到最前面,而不是随意选择交换的位置。为什么这样做是合理的呢?理由如下:

  • 每次操作最多只能交换相邻的字符,因此我们只能在邻近的字符之间进行交换。
  • 我们需要利用有限的交换次数将 0 推到最前面,而不是做不必要的操作。
  • 通过贪心地选择离当前位置最近的 1 进行交换,最大化了每次操作的效果,避免了浪费操作次数。

优化思考

尽管当前的解法有效,但在处理大规模数据时,可能会遇到性能瓶颈。比如,当前算法每次都要在 01 之间逐个交换,这对于每个 0 来说可能会导致不必要的多次交换。

一种优化思路可能是预处理字符串中每个字符的位置,然后通过选择离当前位置最近的 0 来减少交换次数。可以通过哈希表或其他数据结构来加速查找操作,但这样会增加实现的复杂度,可能并不适合在所有场景下使用。

总结

这道题的核心是利用有限的交换次数,通过贪心算法将 0 尽可能地移到前面,从而形成字典序最小的字符串。通过在遍历过程中逐步调整字符串,能够高效地利用每次交换的机会,实现目标。