LeetCode 1375. 二进制字符串前缀一致的次数
题目描述


题意分析
二进制串最初全为 0,位置编号为 $1$ 到 $n$。
flips是这些编号的一个排列,每一步将指定位置从 0 变成 1。完成第 $t$ 步后,若恰好前 $t$ 位为 1、其余位为 0,就计数一次。
解法:前缀最大编号计数
核心思路
[!blue]
处理完代码下标
i后,因为编号不会重复,已经有且只有i + 1个位置变成 1。维护这些位置的最大编号maxPos,就能判断它们是否恰好填满前缀。如果前缀一致,变成 1 的位置必然是 $1$ 到 $i+1$,最大编号自然为
i + 1。反过来,若maxPos == i + 1,这i + 1个不同位置全部落在只有i + 1个位置的区间内,只能把它完全填满;更后面没有已翻转位置,也就全部仍为 0。因此这一等式既是必要条件,也是充分条件。若
maxPos > i + 1,至少有一个 1 落在目标前缀之外,前缀内必然还缺一个位置;maxPos < i + 1则不可能容纳这么多不同位置。于是每步只需先更新最大编号,再判断一次等式,无需保存或扫描整个二进制串。
解题步骤
- 初始化
maxPos = 0、answer = 0。- 按原顺序扫描
flips,将本次编号并入maxPos。- 比较
maxPos与i + 1,相等则将答案加一。- 全部操作结束后返回答案。
代码实现
class Solution {
public int numTimesAllBlue(int[] flips) {
int maxPos = 0;
int answer = 0;
for (int i = 0; i < flips.length; i++) {
// 先包含本步新编号,再判断是否填满前缀。
maxPos = Math.max(maxPos, flips[i]);
// 排列保证已打开 i 加一个位置,数量追平最大编号则没有空洞。
if (maxPos == i + 1) {
answer++;
}
}
return answer;
}
}
func numTimesAllBlue(flips []int) int {
maxPos := 0
answer := 0
for i, pos := range flips {
// 先包含本步新编号,再判断是否填满前缀。
if pos > maxPos {
maxPos = pos
}
// 排列保证已打开 i 加一个位置,数量追平最大编号则没有空洞。
if maxPos == i+1 {
answer++
}
}
return answer
}
复杂度分析
- 时间复杂度:$O(n)$。
- 空间复杂度:$O(1)$。
关键点总结
[!green]
- 数量来自排列的无重复保证,不能推广到反复翻同一位置的输入。
- 全亮也属于合法前缀。
- 代码下标从 0 开始,位置编号从 1 开始,因此已完成的操作数是
i + 1;初始全零状态不计数。
易错点总结
[!yellow]
- 用当前 flips[i] 而不是历史最大值,不能判断前面是否还有空洞。
- 先判断后更新,会忽略本步刚亮起的位置。
- 排序
flips会改变操作顺序,得到的已不是题目要求统计的过程。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 769. 最多能完成排序的块 | 中等 | 同样利用排列前缀的最大值判断是否已包含完整连续前缀,已翻数量等于最大位置时前缀一致。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!