Leetcode-3499-操作后最大活跃区段数-I
题目
给你一个二进制字符串 s,其中:
'1'表示一个活跃区段。'0'表示一个非活跃区段。
定义一次操作分为两个步骤:
- 选择一个两侧都是
'0'的连续'1'区块,将其全部变为'0'。 - 然后,选择一个两侧都是
'1'的连续'0'区块,将其全部变为'1'。
注意:处理时在 s 前、后各添加一个虚拟的 '1'(即等效字符串 t = '1' + s + '1'),两个虚拟 '1' 不计入最终结果。
你最多可以执行 一次 操作。请你计算并返回操作后活跃区段的最大数量。
示例 1:
1 | 输入:s = "01" |
示例 2:
1 | 输入:s = "0100" |
示例 3:
1 | 输入:s = "1000100" |
示例 4:
1 | 输入:s = "01010" |
提示:
1 <= s.length <= 10^5s[i]为'0'或'1'
思路
操作的本质:等价收益模型
这道题的难点在于阅读理解——需要将题目描述的操作转化为简洁的数学模型。
由于 s 两端各有一个 虚拟 '1' ,所以第一步”选择一个两侧都是 '0' 的 '1' 区块”是有可能发生的:两个虚拟 '1' 中间必须存在 '0' 才能形成有效操作。
一次完整操作的过程如下:
- 第一步:选中一块连续
'1'(前后紧邻'0'),将其变为'0'。此时,这块'1'前后的两个'0'区块被「打通」,合并为一个更大的'0'区块。 - 第二步:将合并后的大
'0'区块(此时它的两侧都是'1')全部变为'1'。
两步合在一起,净效果等价于:
选定两个相邻的
'0'区块(中间至少隔有一个'1'区块),将它们全部变成'1'。
中间那块 '1' 在第一步被变为 '0' 后,在第二步又被变了回来,因此实际上并没有损失。换言之:
1 | 操作带来的活跃区段增益 = 相邻两个零块的长度之和 |
最终答案就是:
1 | 答案 = 原始字符串中 '1' 的数量 + 最大相邻零块和 |
如果整串中零块数量不足 2 个(无法形成「两个相邻零块」),则无法执行有效操作,答案为原始 '1' 的数量。
举例分析
以 s = "1000100" 为例,我们给字符串加上虚拟 '1':
1 | 虚拟串 t = "1" + "1000100" + "1" = "110001001" |
零块分别为(只关注原始字符串部分):"000"(长度 3)和 "00"(长度 2),中间隔着一个 '1' 区块。
| 步骤 | 字符串状态 | 说明 |
|---|---|---|
| 初始化 | 110001001 |
虚拟串,加粗的是虚拟 '1' |
| 第一步 | 100000001 |
将中间 '1' 变 '0',两个零块合并为 "00000" |
| 第二步 | 111111111 |
将合并零块变 '1',中间 5 个变成 '1' |
最终活跃区段增量 = 3 + 2 = 5。加上原有的 2 个 '1',答案为 7。
算法设计
核心工作变为:找到所有零块,求相邻零块之和的最大值。
整体分三步:
- 统计原始字符串中
'1'的总数,作为答案基数。 - 遍历字符串,提取所有连续
'0'区块的长度。 - 遍历零块列表,求相邻两项之和的最大值,即为操作收益。若零块不足 2 个则收益为 0。
代码
基础版(两次遍历,O(n) 空间)
直观做法:第一遍统计 '1' 总数,第二遍提取零块长度存入列表,第三遍求相邻零块的最大和。
1 | class Solution { |
复杂度:时间 O(n),空间 O(n)(零块列表最多存储 n 个元素)。
进阶版(一次遍历,O(1) 空间)
观察基础版:求相邻零块和时,每次只需要当前零块和上一个零块的长度,不需要保留全部列表。因此可以在分段遍历的同时,用 prevZero 记住上一个零块长度,边遍历边更新最大增益。
1 | class Solution { |
变量说明:
| 变量 | 作用 |
|---|---|
ones |
统计原始字符串中所有 '1' 的数量,作为答案的基数 |
maxGain |
记录「相邻零块长度之和」的最大值,即操作能带来的最大增益 |
prevZero |
上一个零块的长度。初始为 0,表示尚未遇到零块,顺带处理了「不足 2 个零块」的边界 |
runLen |
当前连续相同字符段的长度计数器 |
复杂度:时间 O(n),空间 O(1)。
遍历时采用「分段统计」技巧:当 s[i] != s[i+1] 或到达末尾时,[i - runLen + 1, i] 就是一个完整的同字符连续段,根据字符类型更新对应变量。
关键点
- 题意转化:理解「先消
'1'再补'0'」的两次操作等价于「直接把两个相邻零块变成'1'」,中间'1'先删后补,互相抵消。这是本题最核心的洞察。 - 虚拟
'1'的作用:保证两端的零块也能被”相邻'1'“包围,从而使第二步操作合法。但虚拟位不计入答案。 - 不可操作的情况:当零块数量 < 2 时(
prevZero始终为 0),maxGain保持为 0,答案即为原始'1'的总数。 - 一次遍历:利用分段统计的技巧,无需显式存储所有零块长度,只需保留
prevZero,将空间优化到O(1)。
相关题目
- 3355. 零数组变换 I — 类似的一次遍历 + 前缀状态维护。
- 1653. 使字符串平衡的最少删除次数 — 字符串分段遍历 + 贪心策略。