题目解析(三)| 豆包MarsCode AI刷题

158 阅读4分钟

题目解析

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

image.png 例如,当前有字符串 "01010",且最多允许 2 次相邻字符交换。小U通过这 2 次交换操作,可以将字符串调整为 "00101",这是可以通过不超过 2 次操作得到的字典序最小的字符串。

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

输入输出格式

  • 输入:三个参数:一个整数 n 表示字符串的长度,一个整数 k 表示最多可进行的交换次数,一个字符串 s 由0和1组成。
  • 输出:返回一个字符串,表示经过最多 k 次操作后得到的字典序最小的字符串。

示例

示例1
输入:
n = 5, k = 2, s = "01010"

输出:
'00101'
解释:经过最多 2 次交换操作,字典序最小的字符串是 "00101"。

示例2
输入:
n = 7, k = 3, s = "1101001"

输出:
'0110101'
解释:经过最多 3 次交换操作,字典序最小的字符串是 "0110101"。

示例3
输入:
n = 4, k = 1, s = "1001"

输出:
'0101'
解释:经过最多 1 次交换操作,字典序最小的字符串是 "0101"。

思路分析

为了得到字典序最小的字符串,我们的目标是将字符串中尽可能多的 0 移到前面,尽量减少 1 出现的位置。每次可以交换相邻的字符,因此我们可以从左到右遍历字符串,并尽可能将 1 后面的 0 移到前面。每次移动都要检查剩余的交换次数 k 是否足够。

解题步骤

  1. 遍历字符串:从左到右扫描每个字符。
  2. 尝试将 0 移到前面:对于每个 1,我们尽可能将它后面的 0 移到它前面,直到交换次数用完或者无法再交换。
  3. 交换操作:每次交换都要检查是否还剩余可用的交换次数 k,并减少 k
  4. 输出结果:最终输出经过最多 k 次交换后的字符串。

代码实现

def solution(n: int, k: int, s: str) -> str:
    s = list(s)  # 将字符串转换为列表以便进行交换操作
    for i in range(n):
        if s[i] == '1':
            # 找到 '1' 后,尝试将其向前移动
            j = i
            while j > 0 and k > 0 and s[j-1] == '0':
                # 交换 s[j] 和 s[j-1]
                s[j], s[j-1] = s[j-1], s[j]
                j -= 1
                k -= 1
            if k == 0:
                break
    return ''.join(s)

思路说明

  • 转换为列表:首先将字符串转换为列表,因为字符串是不可变的,使用列表可以方便地交换字符。
  • 从左到右遍历:我们从左到右遍历每个字符,如果遇到 '1',就尝试将它后面的 '0' 向前交换。每次交换时,都减少可用的交换次数 k
  • 交换相邻元素:如果当前字符是 '1',且其前一个字符是 '0',并且还有剩余的交换次数,则交换这两个字符,直到 k 次操作用完或无法交换为止。
  • 字符串拼接:最终将列表转换回字符串并返回。

时间复杂度

  • 时间复杂度:O(n * k),其中 n 是字符串的长度,k 是最大交换次数。在最坏情况下,我们需要逐个字符检查每次交换是否可行,导致复杂度为 O(n * k)
  • 空间复杂度:O(n),因为我们将字符串转换为列表以便进行交换操作。

边界情况

  1. 如果 k 为 0,说明不能进行任何交换,直接返回原始字符串。
  2. 如果字符串已经是字典序最小的,不需要进行任何操作,直接返回原始字符串。
  3. 如果所有字符都是 01,则字符串本身已经是最优字典序。

通过上述思路和代码,我们能够高效地计算出经过最多 k 次操作后得到的字典序最小的字符串。