二进制字符串重新安排顺序需要的时间

标签: 字符串 动态规划 模拟

难度: Medium

给你一个二进制字符串 s 。在一秒之中,所有 子字符串 "01" 同时 被替换成 "10" 。这个过程持续进行到没有 "01" 存在。

请你返回完成这个过程所需要的秒数。

示例 1:

输入:s = "0110101"
输出:4
解释:
一秒后,s 变成 "1011010" 。
再过 1 秒后,s 变成 "1101100" 。
第三秒过后,s 变成 "1110100" 。
第四秒后,s 变成 "1111000" 。
此时没有 "01" 存在,整个过程花费 4 秒。
所以我们返回 4 。

示例 2:

输入:s = "11100"
输出:0
解释:
s 中没有 "01" 存在,整个过程花费 0 秒。
所以我们返回 0 。

提示:

  • 1 <= s.length <= 1000
  • s[i] 要么是 '0' ,要么是 '1'

进阶:

你能以 O(n) 的时间复杂度解决这个问题吗?

Submission

运行时间: 28 ms

内存: 16.7 MB

class Solution:
    def secondsToRemoveOccurrences(self, s: str) -> int:
        res, cnt = 0, 0
        for c in s:
            if c == '0': cnt += 1
            elif cnt: res = max(res + 1, cnt)
        return res

Explain

题解的思路是通过一次遍历二进制字符串来确定需要的时间。遍历中,用一个计数器cnt记录遇到的'0'的数量。当遇到'1'时,如果之前有'0'(即cnt不为0),则说明存在'01'结构,这时更新结果res为res + 1或cnt的较大值,这表示至少需要这么多秒来使所有'01'变为'10'。最终,cnt记录了需要处理的'01'的个数,而res记录了完成这些转换所需要的秒数。

时间复杂度: O(n)

空间复杂度: O(1)

class Solution:
    def secondsToRemoveOccurrences(self, s: str) -> int:
        res, cnt = 0, 0  # res用来记录结果,cnt用来记录'0'的个数
        for c in s:  # 遍历字符串s中的每个字符
            if c == '0':  # 如果字符是'0'
                cnt += 1  # 增加'0'的计数
            elif cnt:  # 如果遇到'1'且之前有'0'
                res = max(res + 1, cnt)  # 更新所需秒数
        return res  # 返回总秒数

Explore

这种更新逻辑基于最少时间原则,确保所有的'01'都被转换为'10'。每当遇到'1'且之前有'0',意味着我们至少需要一次操作来开始转换这些'0'。`res + 1`表示在已经完成的操作基础上,开始新一轮的转换,而与`cnt`对比确保累积的'0'数量也被考虑在内,因为每个'0'可能需要单独的操作次数。

这种更新方式考虑了到目前为止遇到的所有'0'的累积效应。每次遇到'1'时,如果之前有累积的'0',则至少需要一次额外的操作,这反映了每次'01'对遇到的模式需要单独处理的事实。通过每次选择`res + 1`或`cnt`中的较大值,我们可以保证所有的'01'模式都能够被转换,且不会低估所需的操作次数,从而确保计算出来的是处理所有'01'模式所需的最小秒数。

如果字符串`s`以多个'0'开始,并且后续全是'1',那么这些'0'会显著影响`res`的最终值。因为每一个'0'都需要与之后的'1'配对来形成'01',并被转换为'10'。这意味着每个'0'都会在计数器`cnt`中被记录,每遇到一个'1',计数器就会触发一次更新`res`的操作,最终`res`的值将等于最初累积的`cnt`值,即所有'01'都转换完毕所需的总时间。

在算法中,选择在遇到'1'时考虑之前的'0'的计数`cnt`是因为转换'01'为'10'是算法的核心要求。只有当'0'后面有'1'时,'01'结构才存在并需要被转换。因此,用`cnt`来计数'0',并在遇到'1'时通过更新`res`来考虑这些'0',是确保每个'01'都被计算进转换时间的有效方法。这种设计通过保持对过去状态的跟踪(即`cnt`累积的'0'的数目),确保每个需要转换的'01'都能及时并正确地更新所需的操作次数。