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

155 阅读3分钟

题目解析

本文主要讲解一道来自豆包 MarsCode AI 刷题平台的题目“字典序最小的01字符串”,题目要求我们通过最多 k 次相邻字符交换操作,使得一个由 01 组成的字符串字典序最小化。

题目描述

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

输入与输出

输入:一个整数 n 表示字符串长度,一个整数 k 表示最多可以进行的操作数,字符串 s01 组成。
输出:字典序最小的字符串。

样例

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

关键思路

为解决这个问题,我们需要在 k 次相邻交换操作的限制下,尽可能多地将 0 移动到字符串的最前面,从而使字符串字典序最小。为了实现这一目标,可以采用如下步骤:

  1. 贪心策略:从左向右遍历字符串,当遇到字符 0 时,尽可能将它往前移动。
  2. 计算所需交换次数:对于每个 0,计算将它移动到前面所需的最少交换次数。假设 0 在索引 i 处,则需要与它前面的 1 进行 i - j 次交换操作。
  3. 更新操作数:每次成功交换后,减少 k 中相应的交换次数,直到 k 次操作耗尽或者字符串无法再做进一步优化。

代码实现

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

public class Main {
    public static String solution(int n, int k, String s) {
        char[] chars = s.toCharArray();
        
        for (int i = 0; i < n && k > 0; i++) {
            // 如果当前字符是 '1',跳过
            if (chars[i] == '1') continue;
            
            // 查找可以交换的 '1',并根据剩余操作数确定交换位置
            int j = i;
            while (j > 0 && chars[j - 1] == '1' && k > 0) {
                // 交换 '0' 和前面的 '1'
                chars[j] = '1';
                chars[j - 1] = '0';
                j--;
                k--;  // 减少一次操作
            }
        }
        
        return new String(chars);
    }

    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"));
    }
}

代码解释

solution 函数首先将字符串转为字符数组,以便对字符进行交换操作。接着,从左到右遍历字符数组,当遇到 0 时,尽量将它与前面的 1 交换,直到 k 次操作耗尽或无法进一步优化。最后将字符数组转为字符串并返回。

知识总结

在学习和解决此题的过程中,我总结了以下几点关键知识:

  1. 贪心算法:贪心算法在解决最优序列问题时非常有效,尤其适用于局部最优逐步转化为全局最优的情况。
  2. 字符串操作技巧:字符串转换为字符数组后,可以更高效地操作每个字符。此方法尤其适合需要多次交换的情况。
  3. 交换次数的有效计算:对于每次交换,精确地减少 k 值,确保在操作次数耗尽前实现最优排序。

通过这道题目,我不仅加深了对贪心算法的理解,还学习了如何通过局部最优策略逐步构建出字典序最小的字符串。