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

33 阅读3分钟

分析问题

  1. 理解问题

    • 我们有一个由'0'和'1'组成的字符串。
    • 我们可以进行最多k次相邻字符交换操作。
    • 目标是使字符串的字典序最小。
  2. 贪心策略

    • 从字符串的第一个字符开始,尝试将'0'尽可能地移到前面。
    • 每次找到一个'0',计算将其移到当前位置所需的最小交换次数。
    • 如果交换次数不超过k,则进行交换;否则,继续检查下一个字符。
  3. 具体步骤

    • 遍历字符串,记录当前已经处理的字符数(即已经确定位置的字符数)。
    • 对于每个字符,如果它是'0',计算将其移到当前已处理字符的末尾所需的最小交换次数。
    • 如果交换次数不超过k,则进行交换,并更新k和已处理字符数。
    • 如果交换次数超过k,则跳过该字符,继续处理下一个字符。

代码实现

#include <iostream>
#include <vector>
#include <string>

using namespace std;

string solution(int n, int k, string s) {
    for (int i = 0; i < n && k > 0; i++) {
        if (s[i] == '0') continue;
        
        // Try to move the closest '0' to the front within k swaps
        int j = i + 1;
        while (j < n && s[j] != '0' && j - i < k) {
            j++;
        }
        
        if (j < n && s[j] == '0') {
            // Swap until we move '0' to position i
            for (int x = j; x > i; x--) {
                swap(s[x], s[x - 1]);
            }
            k -= (j - i);
        }
    }
    return s;
}

int main() {
    cout << (solution(5, 2, "01010") == "00101") << endl;
    cout << (solution(7, 3, "1101001") == "0110101") << endl;
    cout << (solution(4, 1, "1001") == "0101") << endl;
    return 0;
}

代码解释

  1. 扫描字符串:我们从左到右扫描字符串 s,每遇到一个 1,就尝试找到它后面的一个 0 并交换到当前 1 的位置,直到剩余的操作次数 k 不能支持继续交换为止。
  2. 交换操作:每次找到可以交换的 0,就将其逐步往前交换,直到它到达当前 1 的位置,消耗的操作次数是交换步数(即 j - i)。
  3. 结束条件:若找不到合适的 0k 次操作已经用完,则直接返回结果。

时间复杂度:

  • 每次操作最多扫描一部分字符串来找到合适的 0,最坏情况下遍历整个字符串多次,因此时间复杂度大致为 O(n * k)

工具运用:

个人建议话题这块还是别太依赖于AI工具,像数据结构与算法这块,就是要自己多加思考,多动手实践,这样才能提升自我,先自己想,想不出来再用AI给思路,如果有思路了就自己往下写,不用怕写错,因为可以借助ai帮忙检查代码,效率翻倍,如果又没思路了就借助ai给点提示(比如伪代码框架)把逻辑理清楚,这对小白是非常友好的;

当然对于大佬来说,可以直接让ai帮忙写代码,关键是,有些题目ai写的代码有些问题,数据无法跑通,这可能是ai对于题目的理解错了,或者代码哪里有问题,这时候就可以自己去人工校验了,找出问题所在并解决,这何尝不是一种提升。


知识点总结

这题也可以用暴力枚举,但这样往往会降低效率,为了提高效率,我采用了贪心算法:贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。