青训营X豆包MarsCode:技术训练营 | 字典序最小的01字符串

30 阅读4分钟

贪心算法是一种在每一步选择当前看起来最佳的选择,不考虑以后可能产生的影响的算法思想。它通常是解决一些最优化问题的有效方法,尽管不能保证得到全局最优解,但大多数时候都能得到近似最优解。

贪心算法的一般步骤如下:

1.构建一个最优化问题的解空间。 2.确定一个约束函数,用于检测一个给定的解是否满足问题的约束条件。 3.为了求解最优化问题,设计一个贪心策略,即求解问题时所遵循的原则。 4.根据贪心策略,在每一步都做出当时看起来是最佳选择的操作,最终得到一个解。

以下是一个经典的贪心算法例子:

背包问题:给定一个背包能够携带的最大重量W,以及N件物品,每件物品有一个重量weight[i]和一个价值value[i],现在让你装入背包,使得装入背包中物品的总价值最大。

贪心策略:按照单位重量价值从大到小的顺序放入背包,直到放满为止。

算法步骤:

1.对所有物品按照单位重量价值从大到小进行排序。 2.从前往后扫描排序后的物品,如果当前物品能够放入背包,就放入;否则跳过该物品。 3.直到无法继续放入物品或者背包放满为止。

这种贪心策略的原因是:我们每次取价值最大的物品放入背包,可以使背包中物品的总价值尽可能地大。尽管它不能保证得到全局最优解,但大多数情况下效果都很不错。

问题描述

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

例如,小U当前有一个字符串 01010,她最多可以进行 2 次相邻字符交换操作。通过这些操作,她可以将字符串调整为 00101,这是可以通过不超过2次操作得到的字典序最小的字符串。

现在,小U想知道,经过最多k次操作后,能够得到的字典序最小的字符串是什么。


测试样例

样例1:

输入:n = 5, k = 2, s = "01010"
输出:'00101'

样例2:

输入:n = 7, k = 3, s = "1101001"
输出:'0110101'

def solution(n: int, k: int, s: str) -> str:
    s = list(s)
    for i in range(n):
        pos = i
        for j in range(i + 1, n):
            if j - i > k:
                break
            if s[j] < s[pos]:
                pos = j
        for j in range(pos, i, -1):
            s[j], s[j - 1] = s[j - 1], s[j]
            k -= 1
    return ''.join(s)
  1. 将输入字符串转换为列表:这样做的目的是为了方便原地修改字符串,因为字符串是不可变对象。
  2. 外层循环遍历字符串的每个位置i:对于每个位置i,我们需要找到从i开始的最小字符,并将其与第i个字符交换。
  3. 内层循环从i+1开始向后查找:为了找到从i开始的最小字符,我们从i+1开始向后查找,直到找到更小的字符或者超出了剩余交换次数的范围。
  4. 更新pos为当前找到的最小字符的位置:如果找到了比当前pos位置字符更小的字符,就更新pos为该字符的位置。
  5. 从pos到i逆向交换相邻字符:在找到最小字符的位置pos后,我们从pos开始,逆向交换相邻字符,直到将最小字符交换到第i个位置。同时,每次交换都减少剩余交换次数k。
  6. 将列表转换回字符串并返回:在完成所有交换后,将修改后的列表转换回字符串并返回。

这个算法的时间复杂度为O(n^2),因为对于每个位置i,我们都需要从i+1开始向后查找最小字符,最坏情况下需要遍历整个剩余字符串。