目录
【算法题解析】用有限操作变换二进制字符串,求能得到的最大二进制字符串
【算法题解析】用有限操作变换二进制字符串,求能得到的最大二进制字符串
题目描述
给你一个由字符 '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 (
"00"
->"10"
)
-
- 可以将连续的两个0中的第一个0变成1,第二个0变成0。
- 这相当于把连续的0“拆散”,减少连续的0,同时字符串中1的数量增多。
- 重要:这操作减少连续的0,使得最终不会有连续的两个0。
- 操作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的位置。
- 代码实现简洁,时间复杂度线性,非常高效。