题解报告:利用有限次数的交换操作实现字典序最小化
题目描述
给定一个由字符 0 和 1 组成的字符串,小U可以进行最多 k 次操作,每次操作可以交换相邻的两个字符。目标是通过不超过 k 次的操作,使得字符串的字典序尽可能小。
解题思路
本题的核心思想是将字符串中的 0 尽量向前移动(靠左),因为 0 的位置越靠前,字符串的字典序越小。在此过程中,需要满足以下约束:
- 每次只能交换相邻字符;
- 交换次数总和不得超过
k。
具体的步骤如下:
- 遍历字符串,每遇到字符
0时,尽量将其向左移动。 - 每次移动时,确定能够交换的位置数目(受限于已有的
1和剩余的操作次数k)。 - 根据上述条件,计算
0可以移动的实际步数,并执行交换操作,同时减少相应的k值。 - 如果某次交换操作耗尽了
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; // 遍历完成后返回最终结果
}
代码解释
- 变量定义:
len:用于记录当前遍历位置之前遇到的1的个数。k:剩余的可操作次数。
- 遍历字符串:
- 遇到
1时,更新len的值; - 遇到
0时,计算该0可以向左移动的步数:- 如果剩余的
k值足够覆盖len,说明可以完全将0移到所有的1前; - 如果
k值不足,则只能部分移动,将0移动到尽可能靠前的位置。
- 如果剩余的
- 遇到
- 终止条件:
- 当剩余
k值为 0 时,直接返回当前字符串。 - 否则,遍历完成后返回最终结果。
- 当剩余
时间复杂度分析
该算法的时间复杂度为 (O(n)),其中 (n) 为字符串的长度。
- 每次遍历字符串时,仅需要更新变量
len和k,以及执行 (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;
}
- 输入:
01010,k=2,输出:00101。 - 输入:
1101001,k=3,输出:0110101。 - 输入:
1001,k=1,输出:0101。
测试结果表明,算法能够正确计算出经过最多 k 次操作后得到的字典序最小的字符串。