MarsCode AI 刷题思路解析:19. (难) 字典序最小的01字符串 | 豆包MarsCode AI刷题

45 阅读4分钟

题目描述

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

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

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

思路分析

问题理解

首先,我们需要理解什么是字典序最小。字典序最小意味着在字符串中,'0' 应该尽可能地出现在 '1' 之前。例如,字符串 01010 的字典序最小形式是 00101。我们需要通过最多 k 次相邻字符交换操作,使得字符串的字典序最小。关键在于如何有效地找到并交换 '0' 和 '1',使得 '0' 尽可能地出现在 '1' 之前。

数据结构的选择

为了实现这个目标,我们可以使用以下数据结构和算法:

  1. 字符串列表:将字符串转换为列表,以便进行交换操作。
  2. 优先队列(堆):使用优先队列来快速找到当前最小的字符。

解题思路

  1. 遍历字符串:从左到右遍历字符串。
  2. 找到最近的 '0':对于每个 '1',找到它右边最近的 '0'。
  3. 计算交换次数:计算将这个 '0' 移动到当前 '1' 位置所需的交换次数。
  4. 执行交换:如果交换次数小于等于剩余的操作次数 k,则执行交换。
  5. 更新操作次数:每次交换后,减少可用的操作次数 k

样例分析

假设我们有字符串 1101001k = 3,我们可以通过以下步骤来实现字典序最小:

  1. 初始状态

    字符串:1101001,操作次数:3

  2. 第一次操作

    从堆中取出最小的字符 '0',索引为 2

    计算将其移动到最前面的交换次数:2

    执行交换操作:1101001 -> 0111001

    剩余操作次数:1

  3. 第二次操作

    从堆中取出最小的字符 '0',索引为 4

    计算将其移动到最前面的交换次数:3

    由于剩余操作次数不足,仅可执行 1 次

    执行交换操作:0111001 -> 0110101

  4. 最终输出

    字符串:0110101

代码详解

  1. 初始化s = list(s):将字符串转换为列表,以便进行交换操作。

  2. 遍历字符串

    for i in range(n)::从左到右遍历字符串。

    if k <= 0::如果没有剩余的操作次数,退出循环。

    if s[i] == '0'::如果当前字符是 '0',则无需移动,继续下一个字符。

  3. 找到最近的 '0'

    for j in range(i + 1, n)::从当前字符的右边开始寻找 '0'。 if s[j] == '0'::找到 '0' 后,计算所需的交换次数。

  4. 计算交换次数

    needed_swaps = j - i:计算将 '0' 移动到当前 '1' 位置所需的交换次数。

    if needed_swaps <= k::如果所需的交换次数小于等于剩余的操作次数,则执行交换。

  5. 执行交换

    for swap in range(j, i, -1)::从 '0' 的位置向左交换,直到 '0' 到达当前 '1' 的位置。

    s[swap], s[swap - 1] = s[swap - 1], s[swap]:执行交换操作。

    k -= needed_swaps:减少可用的交换次数。

  6. 返回结果

    return ''.join(s):将列表转换回字符串并返回。

def solution(n: int, k: int, s: str) -> str:
    s = list(s)  # 将字符串转换为列表以便操作
    for i in range(n):
        if k <= 0:
            break  # 如果没有剩余的操作次数,退出
        
        if s[i] == '0':
            continue  # 如果当前字符是 '0',则无需移动
        
        # 需要将字符 '0' 移到当前位置 i 之前
        # 我们寻找能用来替换当前 '1' 的最小的 '0'
        for j in range(i + 1, n):
            if s[j] == '0':
                # 计算需要的交换次数
                needed_swaps = j - i
                if needed_swaps <= k:
                    # 执行交换
                    for swap in range(j, i, -1):
                        s[swap], s[swap - 1] = s[swap - 1], s[swap]
                    k -= needed_swaps  # 减少可用的交换次数
                break  # 找到一个 '0' 之后就可以退出内层循环

    return ''.join(s)

if __name__ == '__main__':
    print(solution(5, 2, "01010") == '00101')
    print(solution(7, 3, "1101001") == '0110101')
    print(solution(4, 1, "1001") == '0101')

运行结果

image.png