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

42 阅读5分钟

题目分析

题目要求我们在给定的字符串中,通过不超过 k 次相邻字符交换操作,将字符串调整为字典序最小的字符串。对于一个由 '0' 和 '1' 组成的字符串,字典序最小的字符串是尽可能多地将 '0' 移动到字符串的前面,尽可能少地让 '1' 出现在前面。因此,问题的核心是在限定操作次数内,通过交换相邻字符的方式,使得字符串的字典序最小。

解题思路

字典序的定义:字典序最小的字符串是包含尽可能多的 '0' 在字符串的最前面,剩下的 '1' 尽可能集中在后面。因此,我们的目标就是尽可能地把 '0' 向前移动,直到操作次数用完。

交换策略:由于每次操作只能交换相邻的字符,最有效的方式是从字符串的左端开始,遇到 '1' 时,尽量将它和后面的 '0' 交换,直到用完允许的操作次数。通过这种方式,我们可以把 '0' 尽量移到最前面。

贪心算法的应用:我们可以使用贪心策略来选择操作。在每次遍历字符串时,我们寻找从当前位置开始,能够移动的最近的 '0',并将它移动到当前位置。每次移动都会消耗一个操作次数,直到操作次数用尽。

操作的限制:每次交换只能交换相邻的字符,因此我们需要计算每个 '0' 能够被移动到的位置,同时考虑剩余的操作次数。在移动 '0' 时,如果剩余的操作次数不足以完成当前交换,就停止移动。

详细步骤

步骤1:遍历字符串 从左到右遍历字符串,对于每个 '1',我们寻找距离它最近的 '0',并进行交换。如果找到的 '0' 能够在剩余的操作次数内被移动到当前位置,就交换它们,并更新剩余操作次数。

步骤2:进行交换操作 对于每个遇到的 '1',将最接近的 '0' 移动到它前面。每次交换操作消耗一次操作次数,并更新剩余的 k。

步骤3:输出结果 最后,字符串经过最多 k 次操作后,就变成了字典序最小的字符串,输出该字符串。

代码实现

#include <iostream>
#include <string>
using namespace std;

string solution(int n, int k, string s) {
    // 遍历每个字符,尽量将0移动到前面
    for (int i = 0; i < n && k > 0; ++i) {
        // 找到第一个'1',并且尝试将它与后面的'0'交换
        if (s[i] == '1') {
            for (int j = i + 1; j < n && s[j] == '0' && k > 0; ++j) {
                // 交换s[i]和s[j]
                swap(s[i], s[j]);
                k--;  // 每交换一次消耗一次操作
                i = j;  // 更新当前位置
                break;
            }
        }
    }
    return s;
}

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

贪心策略:我们从字符串的左端开始,如果遇到 '1',就找它后面最近的 '0' 并进行交换,直到操作次数用完。 交换过程:通过内层的 for 循环,我们查找当前 '1' 后面最接近的 '0',并通过 swap 操作将它们交换。在每次交换后,我们减少操作次数 k,并更新当前的字符位置。 效率:由于每次只进行一次交换,因此时间复杂度大约是 O(n),其中 n 是字符串的长度。在最坏情况下,我们需要遍历字符串中的每个字符并进行一次交换。

本题的解决思路

主要依赖于贪心算法。我们通过每次将 '0' 尽可能地移到前面,来实现字典序最小的目标。由于交换只能是相邻字符的交换,因此我们需要在每次遇到 '1' 时,尽量将其后面的 '0' 移到前面,直到操作次数 k 用尽。

个人思考

贪心思想的应用:这道题让我更加理解了贪心策略的实际应用。在这个问题中,贪心地选择将 '0' 移动到前面,能够达到最优解。每次尽可能地做出最优的局部选择(即移动最近的 '0'),并且确保每个选择都是可行的。

操作次数的限制:题目中的操作次数 k 是一个重要的限制因素。在实际应用中,如何在有限的资源(如操作次数)下尽量优化结果是一个常见的优化问题。通过贪心策略,我们确保在每次操作时都能最大化地利用剩余操作次数。

边界情况的处理:在代码实现中,我们需要特别注意操作次数 k 可能提前耗尽的情况。为了避免不必要的交换,我们在每次交换前检查剩余的操作次数是否足够。

优化空间:虽然本解法是一个有效的贪心策略,但在更复杂的问题中,我们可能需要考虑更加优化的方案,如使用优先队列(堆)来提前确定应该交换的位置。对于这种字符串变换类的问题,贪心算法通常是一个不错的起点。