LC每日一题|20240410 - 1702. 修改后的最大二进制字符串
给你一个二进制字符串
binary,它仅有0或者1组成。你可以使用下面的操作任意次对它进行修改:
操作 1 :如果二进制串包含子字符串
"00",你可以用"10"将其替换。比方说,
"00010" -> "10010"操作 2 :如果二进制串包含子字符串
"10",你可以用"01"将其替换。比方说,
"00010" -> "00001"请你返回执行上述操作任意次以后能得到的 最大二进制字符串 。如果二进制字符串
x对应的十进制数字大于二进制字符串y对应的十进制数字,那么我们称二进制字符串x大于二进制字符串y。
提示:
1 <= binary.length <= 10^5binary仅包含'0'和'1'。
题目等级:Medium
理解题目
我们的目标是得到最大的二进制串,直觉上应该是将 0 尽可能转化为 1 ,或者将 1 尽可能前移。
再看一下题中的两个操作:
- 对于
操作1,将00变成10后,相当于凭空变出来一个1,二进制串变大了,满足我们的期望。 - 对于
操作2,将10变成01后,相当于把1后移了,二进制串是变小的,好像并不符合我们的期望。
那操作2存在的意义是什么呢?是把 0 前移!
举个🌰
我们带入题目中给出的case: 000110。
0 -- 0 -- 0 -- 1 -- 1 -- 0
1 -- 0 -- 0 -- 1 -- 1 -- 0 // 对
下标0和下标1进行操作11 -- 1 -- 0 -- 1 -- 1 -- 0 // 对
下标1和下标2进行操作1
此刻我们已经没有执行操作1的空间了。因为 下标2 处的 0 后边已经没有另一个 0可以让它执行 操作1 了。
而恰好,操作2 可以帮我们把 0 移到前边来。
比如对于case 110,可以通过执行两次 操作2 将其变成 011:
1 -- 1 -- 0
1 -- 0 -- 1 // 对
下标1和下标2进行操作20 -- 1 -- 1 // 对
下标0和下标1进行操作2
套入原case:
1 -- 1 -- 0 -- 0 -- 1 -- 1 // 将
下标3处的110通过操作2变成0111 -- 1 -- 1 -- 0 -- 1 -- 1 // 对
下标2和下标3进行操作1
对于 下标3 ,此时已经没有更多的 0 借来做 操作1 了,此时我们就得到了正确答案:111011
总结一下
我们可以贪心的从前向后执行 操作1 。在遇到 01 的时候利用 操作2 尝试从后边借一个 0 过来再执行 操作1 。如果借用成功,则该 01 变成 10,同时通过 操作2 借出 0 的位置变成了1;如果没有 0 可借,则证明没有其他操作空间,可直接返回。
我们可以提前保存一下0出现的位置。
AC代码
class Solution {
fun maximumBinaryString(binary: String): String {
val position = ArrayList<Int>() // 0的位置
for (i in binary.indices) if (binary[i] == '0') position.add(i)
if (position.size <= 1) return binary // 如果最多只有1个0,则没有操作空间,直接返回
val ca = binary.toCharArray()
for (i in 0 until position.size - 1) { // 最后一个0没有其他0可借,直接退了
ca[position[i]] = '1' // 操作1的第0个下标变成1
ca[position[i + 1]] = '1' // 操作2借出0的位置变成1
ca[position[i] + 1] = '0' // 操作1的第1个下标变成0
position[i + 1] = position[i] + 1 // 将操作1新生成的0保存到position中,以供下轮遍历
}
return ca.joinToString("")
}
}
时间复杂度: O(N),需要两次遍历
空间复杂度: O(N),需要保存0的位置和输出结果