字典序最小的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',我们尝试将它与尽可能远的'0'交换,这样可以使得'1'尽可能地靠后,从而使得字符串的字典序更小。
- 交换次数限制:我们有最多k次交换机会,因此每次交换都需要考虑是否超出了这个限制。
- 边界条件:在交换时,我们需要考虑字符串的边界,即不能超出字符串的长度。
- 提前终止:如果在交换过程中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')
复杂度分析
时间
- 外层循环:算法中有一个外层循环,它遍历字符串中的每个字符。这个循环的时间复杂度是O(n),其中n是字符串的长度。
- 内层循环:对于外层循环中的每个'1',我们启动一个内层循环来寻找可以交换的'0'。在最坏的情况下,这个内层循环可能需要遍历到字符串的末尾,因此它的时间复杂度也是O(n)。然而,由于内层循环是在找到第一个'0'后就终止的,平均情况下它的运行时间会小于O(n)。
- 交换操作:每次交换操作的时间复杂度是O(1),因为它只涉及两个元素的交换。
- 总时间复杂度:综合考虑,算法的总时间复杂度是O(n^2)。这是因为在最坏的情况下,每个'1'都可能需要进行n次比较来找到可以交换的'0',而字符串中有n个位置可能包含'1'。因此,算法的时间复杂度与字符串长度的平方成正比。
空间复杂度
- 输入数据:算法的输入是一个长度为n的字符串和一个整数k。这些输入数据本身需要O(n)的空间。
- 辅助空间:算法中使用了额外的辅助空间来存储字符列表
s_list和变量k。字符列表s_list的空间复杂度是O(n),因为它需要存储整个字符串。变量k只需要常数空间O(1)。 - 总空间复杂度:因此,算法的总空间复杂度是O(n),这是因为我们需要存储整个字符串的副本。