题目解析 最小步数归零问题 | 豆包MarsCode AI刷题

138 阅读4分钟

字典序最小的01字符串

问题链接

问题描述

小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'

样例3:

输入:n = 4, k = 1, s = "1001"
输出:'0101'

算法分析

这个问题是一个典型的贪心算法问题。目标是通过交换相邻字符,使得字符串的字典序最小。 我从以下几点考虑这道题:

  1. 贪心策略:对于字符串中的每一个'1',我们尝试将它与尽可能远的'0'交换,这样可以使得'1'尽可能地靠后,从而使得字符串的字典序更小。
  2. 交换次数限制:我们有最多k次交换机会,因此每次交换都需要考虑是否超出了这个限制。
  3. 边界条件:在交换时,我们需要考虑字符串的边界,即不能超出字符串的长度。
  4. 提前终止:如果在交换过程中k次机会已经用完,那么我们就可以提前终止循环。

代码实现

def solution(n: int, k: int, s: str) -> str:
    s_list = list(s)  # 将字符串转换为列表以便交换

    for i in range(n):
        if s_list[i] == '1':
            # 找到最远的0的位置,且在k次交换范围内
            for j in range(i, min(n, i + k + 1)):  
                if s_list[j] == '0':

                    s_list[i], s_list[j] = s_list[j], s_list[i]
                    k -= j - i  
                    break  # 找到一个0交换后即停止,因为已经将当前1移动到最远的0的位置
            if k == 0:  # 如果k用完,提前退出
                break

    return ''.join(s_list)  

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

复杂度分析

时间

  1. 外层循环:算法中有一个外层循环,它遍历字符串中的每个字符。这个循环的时间复杂度是O(n),其中n是字符串的长度。
  2. 内层循环:对于外层循环中的每个'1',我们启动一个内层循环来寻找可以交换的'0'。在最坏的情况下,这个内层循环可能需要遍历到字符串的末尾,因此它的时间复杂度也是O(n)。然而,由于内层循环是在找到第一个'0'后就终止的,平均情况下它的运行时间会小于O(n)。
  3. 交换操作:每次交换操作的时间复杂度是O(1),因为它只涉及两个元素的交换。
  4. 总时间复杂度:综合考虑,算法的总时间复杂度是O(n^2)。这是因为在最坏的情况下,每个'1'都可能需要进行n次比较来找到可以交换的'0',而字符串中有n个位置可能包含'1'。因此,算法的时间复杂度与字符串长度的平方成正比。

空间复杂度

  1. 输入数据:算法的输入是一个长度为n的字符串和一个整数k。这些输入数据本身需要O(n)的空间。
  2. 辅助空间:算法中使用了额外的辅助空间来存储字符列表s_list和变量k。字符列表s_list的空间复杂度是O(n),因为它需要存储整个字符串。变量k只需要常数空间O(1)。
  3. 总空间复杂度:因此,算法的总空间复杂度是O(n),这是因为我们需要存储整个字符串的副本。