刷题打卡 | 豆包MarsCode AI 刷题

80 阅读3分钟

题解报告:利用有限次数的交换操作实现字典序最小化

题目描述

给定一个由字符 01 组成的字符串,小U可以进行最多 k 次操作,每次操作可以交换相邻的两个字符。目标是通过不超过 k 次的操作,使得字符串的字典序尽可能小。

解题思路

本题的核心思想是将字符串中的 0 尽量向前移动(靠左),因为 0 的位置越靠前,字符串的字典序越小。在此过程中,需要满足以下约束:

  1. 每次只能交换相邻字符;
  2. 交换次数总和不得超过 k

具体的步骤如下:

  1. 遍历字符串,每遇到字符 0 时,尽量将其向左移动。
  2. 每次移动时,确定能够交换的位置数目(受限于已有的 1 和剩余的操作次数 k)。
  3. 根据上述条件,计算 0 可以移动的实际步数,并执行交换操作,同时减少相应的 k 值。
  4. 如果某次交换操作耗尽了 k 值,则直接返回当前字符串。

实现代码

下面是 C++ 代码实现:

#include <iostream>
#include <string>

using namespace std;

string solution(int n, int k, string s) {
    int len = 0; // 用于记录当前遍历位置之前的 '1' 的数量
    for (int i = 0; i < n; i++) {
        if (s[i] == '1') {
            ++len; // 更新 '1' 的数量
        } else if (s[i] == '0') {
            if (k >= len) {
                // 可以完全把这个 '0' 移到前面所有的 '1' 之前
                swap(s[i], s[i - len]);
                k -= len; // 消耗掉对应的操作次数
            } else {
                // 操作次数不足以完全移动到目标位置,只能移动部分
                swap(s[i], s[i - k]);
                return s; // 剩余次数为 0,返回结果
            }
        }
    }
    return s; // 遍历完成后返回最终结果
}

代码解释

  1. 变量定义
    • len:用于记录当前遍历位置之前遇到的 1 的个数。
    • k:剩余的可操作次数。
  2. 遍历字符串
    • 遇到 1 时,更新 len 的值;
    • 遇到 0 时,计算该 0 可以向左移动的步数:
      • 如果剩余的 k 值足够覆盖 len,说明可以完全将 0 移到所有的 1 前;
      • 如果 k 值不足,则只能部分移动,将 0 移动到尽可能靠前的位置。
  3. 终止条件
    • 当剩余 k 值为 0 时,直接返回当前字符串。
    • 否则,遍历完成后返回最终结果。

时间复杂度分析

该算法的时间复杂度为 (O(n)),其中 (n) 为字符串的长度。

  • 每次遍历字符串时,仅需要更新变量 lenk,以及执行 (O(1)) 的交换操作。

测试结果

以下为测试用例及结果验证:

int main() {
    cout << (solution(5, 2, "01010") == "00101") << endl; // 输出: 1 (true)
    cout << (solution(7, 3, "1101001") == "0110101") << endl; // 输出: 1 (true)
    cout << (solution(4, 1, "1001") == "0101") << endl; // 输出: 1 (true)
    return 0;
}
  • 输入:01010k=2,输出:00101
  • 输入:1101001k=3,输出:0110101
  • 输入:1001k=1,输出:0101

测试结果表明,算法能够正确计算出经过最多 k 次操作后得到的字典序最小的字符串。