如何通过有限操作实现字典序最小|豆包MarsCode AI刷题

54 阅读4分钟

小U的字符串优化:如何通过有限操作实现字典序最小?

问题背景

小U面临一个有趣的问题:她拥有一个由01组成的字符串,并且可以进行最多k次操作,每次操作可以交换相邻的两个字符。目标是通过这些操作,使得最终的字符串字典序最小

举例说明

例如:

  1. 字符串为01010,最多可以进行2次操作。
    经过调整,可以将字符串变为00101,这是可能得到的字典序最小的字符串

  2. 字符串为1101001,最多可以进行3次操作。
    经过调整,可以将字符串变为0110101

思路解析

为了解决这个问题,我们需要仔细思考如何在有限的操作次数下,通过交换字符来尽可能地优化字符串字典序。


解题思路

  1. 核心目标
    字典序最小的字符串意味着更多的0应该尽可能地移动到字符串的左侧。

  2. 局部最优原则
    每次尽量将0左移,但每次移动时只允许交换相邻的字符。因此,0只能逐步向左靠近。

  3. 有限资源约束
    每次交换消耗1次操作,最多可以进行k次操作。当操作次数耗尽时,直接输出当前结果。

  4. 贪心算法
    为了实现上述目标,可以采用贪心策略:

    • 从左到右遍历字符串,遇到0时,尝试将它向左移动,直到:
      • 它前面没有1
      • 已经耗尽所有操作次数。

算法实现

下面是该问题的Python代码实现:

def solution(n: int, k: int, s: str) -> str:
    s = list(s)  # 将字符串转为列表,便于操作
    for i in range(n):
        # 尝试将当前字符左移
        j = i
        while j > 0 and s[j] == '0' and s[j - 1] == '1' and k > 0:
            # 交换相邻字符
            s[j], s[j - 1] = s[j - 1], s[j]
            j -= 1  # 向左继续尝试
            k -= 1  # 消耗一次操作
            
            # 如果操作次数用尽,直接返回结果
            if k == 0:
                return ''.join(s)
    return ''.join(s)

测试样例

样例1
  • 输入:n = 5, k = 2, s = "01010"
  • 输出:"00101"

过程分析

  1. 第1步:01010 -> 00110(第2个0与第1个1交换,消耗1次操作)。
  2. 第2步:00110 -> 00101(第3个0与第2个1交换,消耗1次操作)。

最终结果为"00101"

样例2
  • 输入:n = 7, k = 3, s = "1101001"
  • 输出:"0110101"

过程分析

  1. 第1步:1101001 -> 1011001(第2个0与第1个1交换,消耗1次操作)。
  2. 第2步:1011001 -> 0111001(第3个0与第2个1交换,消耗1次操作)。
  3. 第3步:0111001 -> 0110101(第4个0与第3个1交换,消耗1次操作)。

最终结果为"0110101"

样例3
  • 输入:n = 4, k = 1, s = "1001"
  • 输出:"0101"

过程分析

  1. 第1步:1001 -> 0101(第2个0与第1个1交换,消耗1次操作)。

最终结果为"0101"


时间复杂度分析

  1. 外层遍历:从左到右扫描字符串,时间复杂度为O(n)
  2. 内层操作:最多向左交换k次,每次操作最多需要扫描当前索引之前的元素,时间复杂度为O(k)

总体时间复杂度O(n * k),对于常规输入可以快速处理。


贪心策略的优劣

  1. 优点

    • 直接操作字符串,不依赖额外的数据结构。
    • 简单高效,满足大多数输入场景。
  2. 缺点

    • 当字符串长度n和操作次数k极大时,性能可能受到限制。
    • 贪心策略可能会局限于局部最优,而非全局最优(但本问题中,局部最优可以保证全局最优)。

总结

本问题充分利用了贪心算法的优势,通过局部调整一步步逼近全局最优解。在有限资源(操作次数)下,如何优化问题结果是算法设计的关键。通过代码实现,我们验证了贪心策略的有效性,希望这篇博客能帮助大家更好地理解字符串优化问题中的算法设计!