字典序最小的01字符串

133 阅读5分钟

问题描述

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

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

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

代码的目的

这段代码实现了一个函数solution,该函数用于在给定的字符串中通过有限的字符交换产生字典序最小的字符串。字典序是指在字母表中字符排列的顺序,比如 '0' < '1' < 'a' < 'b'。

函数定义

def solution(n: int, k: int, s: str) -> str:  
  • n:字符串的长度。
  • k:可以进行的最大交换次数。
  • s:输入的字符串。

字符串转列表

    s = list(s)  # 转换字符串为列表以便进行字符交换  

将字符串 s 转换为字符列表。由于字符串是不可变的,转换为列表后可以方便地进行字符间的交换。

主循环

    for i in range(n):  
  • 外层循环遍历字符串的每个字符,通过索引 i 来标识当前处理的字符。
        if k <= 0:  # 如果没有剩余的操作次数,退出  
            break  
  • 检查剩余的交换次数 k 是否小于等于 0。如果是,意味着没有交换的机会,直接退出循环。

寻找最小字符的位置

        min_pos = i  
  • 初始化 min_pos 为当前字符的索引 i,这用于记录后续字符中最小字符的位置。
        for j in range(i + 1, min(i + k + 1, n)):  # 防止超出边界  
  • 进入内层循环,从 i + 1 到 i + k + 1 来查找在 i 之后,最多用掉 k 次操作可以到达的范围内的最小字符。min(i + k + 1, n) 确保不超过字符串的长度 n
            if s[j] < s[min_pos]:  
                min_pos = j  
  • 对于范围内的每个字符,比较字符的大小。如果找到的字符 s[j] 小于当前 s[min_pos],则更新 min_pos 为 j,即记录更小字符的新位置。

交换字符

        # 如果找到了一个更小的字符,进行相应的交换  
        while min_pos > i and k > 0:  
  • 如果 min_pos 大于 i,说明找到的更小字符的位置在当前字符之后,并且还有交换次数 (k) 剩余,可以进行交换。
            s[min_pos], s[min_pos - 1] = s[min_pos - 1], s[min_pos]  # 交换  
  • 进行交换,将 min_pos 的字符与其前一个字符交换,以将更小的字符向左移动。
            min_pos -= 1  
            k -= 1  # 减少操作次数  
  • 每进行一次交换,min_pos 向前移动一位,k 减一,表示已使用了一次交换机会。

返回结果

    return ''.join(s)  # 返回最终字符串  
  • 在完成所有字符处理后,将列表 s 转换回字符串并返回最终结果。

测试用例

if __name__ == '__main__':  
    print(solution(5, 2, "01010") == '00101')   # 输出: True  
    print(solution(7, 3, "1101001") == '0110101')  # 输出: True  
    print(solution(4, 1, "1001") == '0101')    # 输出: True  
  • 这些测试用例用来验证函数的正确性,检查不同输入下的输出是否符合预期。它们展示了在给定的交换次数下,如何能通过交换操作为字符串生成字典序最小的版本。

总结

该函数的思路是通过贪心算法,利用有限的字符交换次数,使得字符串尽可能向字典序更小的方向排序。这个过程通过在当前字符后寻找最小值并不断交换,确保所得到的结果是尽可能最小的字符串。此算法在保证字典序最小的同时,也较为高效,适用于处理较长字符串的情况。贪心算法简介:贪心算法是一种在每一步选择中都采取当前最优解的算法策略,以期通过一系列局部最优的选择达到全局最优解。其核心思想是“每一步都做出一个局部最优的选择”,从而希望最终能够获得全局最优的结果。

贪心算法特点

  • 简单高效:贪心算法通常比较简单,容易实现,并且运行速度较快。
  • 局部最优:每一步都选择当前最优解,但不一定能得到全局最优解。
  • 不回溯:一旦做出选择,不会回退或重新考虑之前的决策。

完整代码

def solution(n: int, k: int, s: str) -> str:  
    s = list(s)  # 转换字符串为列表以便进行字符交换  
    for i in range(n):  
        if k <= 0:  # 如果没有剩余的操作次数,退出  
            break  
        # 在后续 k 个字符中寻找最小字符的位置  
        min_pos = i  
        for j in range(i + 1, min(i + k + 1, n)):  # 防止超出边界  
            if s[j] < s[min_pos]:  
                min_pos = j  
        
        # 如果找到了一个更小的字符,进行相应的交换  
        while min_pos > i and k > 0:  
            s[min_pos], s[min_pos - 1] = s[min_pos - 1], s[min_pos]  # 交换  
            min_pos -= 1  
            k -= 1  # 减少操作次数  
        
    return ''.join(s)  # 返回最终字符串  

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