1702. 修改后的最大二进制字符串

目录

【算法题解析】用有限操作变换二进制字符串,求能得到的最大二进制字符串

题目描述

示例

题目限制

解题分析

关键观察

二进制字符串大小比较

解题方法

1. 统计关键参数

2. 计算最后一个 0 会停留的位置

3. 构造最终字符串

代码实现

详细示例说明

为什么这个结果是最大?

代码复杂度

代码测试

总结


【算法题解析】用有限操作变换二进制字符串,求能得到的最大二进制字符串


题目描述

给你一个由字符 '0''1' 组成的二进制字符串 binary,你可以对它执行以下两种操作任意次(顺序不限):

  • 操作1:如果字符串中存在子串 "00",你可以将它替换成 "10"
  • 操作2:如果字符串中存在子串 "10",你可以将它替换成 "01"

请你返回经过若干次操作后,可以得到的 十进制值最大的二进制字符串


示例

示例1:

输入:binary = "000110"

可能的操作过程:

"000110" -> "000101" (操作2,把 "10" -> "01")
"000101" -> "100101" (操作1,把 "00" -> "10")
"100101" -> "110101" (操作2)
"110101" -> "110011" (操作1)
"110011" -> "111011" (操作1)

输出:"111011"


示例2:

输入:binary = "01"

无法进行任何操作,输出仍为 "01"


题目限制

  • 字符串长度范围:1 <= binary.length <= 10^5
  • 字符仅包含 '0''1'

解题分析

要找经过多次操作后能得到的最大二进制字符串,必须理解这两种操作对字符串的本质影响:

  1. 操作1 ("00" -> "10")
    • 可以将连续的两个0中的第一个0变成1,第二个0变成0。
    • 这相当于把连续的0“拆散”,减少连续的0,同时字符串中1的数量增多。
    • 重要:这操作减少连续的0,使得最终不会有连续的两个0。
  1. 操作2 ("10" -> "01")
    • 把相邻的 "1" 和 "0" 交换,使得0往字符串右边移动,1往左移动。
    • 不改变字符串中0和1的总数,只调整顺序。

关键观察

  • 操作1是唯一能改变0和1数量的操作,可以把部分0转换成1。
  • 操作2只是调整顺序,使得0向后移动,1向前移动。
  • 多次操作后,字符串中不会存在连续的两个0,否则操作1还可以继续执行。
  • 因此,最终字符串中0之间必被1隔开,最多只有一个连续的0块。

二进制字符串大小比较

二进制数的大小主要取决于:

  • 最高位的数字越大,整个数越大(1 > 0)。
  • 在前面的数字相同的情况下,较后面的数字大小决定。
  • 所以,要使字符串最大,应该让尽可能多的1出现在左边。

解题方法

1. 统计关键参数

  • 统计字符串中 0 的个数:count0
  • 统计字符串中 1 的个数:count1 = len(binary) - count0
  • 找到第一个出现的 0 的位置:first_zero_pos

2. 计算最后一个 0 会停留的位置

经过多次操作1和操作2:

  • 第一个0不会向左移动(它之前都是1,无法移动)。
  • 其余连续的0都被逐步移向更靠右的位置,最终全部连续的0会合并成一个单独的0块。
  • 最后一个0的位置为:
zero_pos = first_zero_pos + count0 - 1

3. 构造最终字符串

  • 将所有 1 放在字符串最左边,直到位置 zero_pos 前。
  • 位置 zero_pos 放一个 0
  • 剩余位置放 1

用代码表达就是:

'1' * zero_pos + '0' + '1' * (len(binary) - zero_pos - 1)

代码实现

class Solution:
    def maximumBinaryString(self, binary: str) -> str:
        count0 = binary.count('0')
        if count0 == 0:
            return binary
        first_zero_pos = binary.index('0')
        zero_pos = first_zero_pos + count0 - 1
        return '1' * zero_pos + '0' + '1' * (len(binary) - zero_pos - 1)

详细示例说明

示例:

binary = "000110"

  • count0 = 3 (字符 '0' 出现了3次)
  • count1 = 3
  • first_zero_pos = 0 (第一个0在字符串开头)

计算:

  • zero_pos = 0 + 3 - 1 = 2

最终字符串构造:

  • '1' * 2 + '0' + '1' * (6 - 2 - 1) = "11" + "0" + "111" = "110111"

二进制值 "110111" 比原字符串的值更大,且满足操作的限制。


为什么这个结果是最大?

  • 字符串最左边尽可能多的1,保证高位的值大。
  • 一个0被“推”到了 zero_pos 位置。
  • 0右边全是1,保证右边尽可能大。
  • 任何其它含有连续0或把0放在更前的位置的字符串都会更小。

代码复杂度

  • 统计0和1的个数,找到第一个0的位置均是 O(n) 时间复杂度。
  • 构造字符串也是 O(n)。
  • 整体算法时间复杂度:O(n),空间复杂度:O(n)。

代码测试

sol = Solution()

print(sol.maximumBinaryString("000110"))  # 输出: "110111"
print(sol.maximumBinaryString("01"))      # 输出: "01"
print(sol.maximumBinaryString("11111"))   # 输出: "11111"
print(sol.maximumBinaryString("0000"))    # 输出: "1110"
print(sol.maximumBinaryString("1001"))    # 输出: "1101"

总结

这道题目通过两个互相配合的字符串替换操作,将多个连续的0“消化”成一个0,并将字符串中尽可能多的字符变成1,同时让0尽可能靠后,最后形成的字符串保证了二进制值最大。

  • 重点在于理解两种操作的本质和相互配合的效果。
  • 利用字符串特征(第一个0的位置和0的数量)精确计算最终0的位置。
  • 代码实现简洁,时间复杂度线性,非常高效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值