LeetCode破解之二进制字符串前缀一致的次数

213 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

题目描述

给你一个长度为 n 、下标从 1 开始的二进制字符串,所有位最开始都是 0 。我们会按步翻转该二进制字符串的所有位(即,将 0 变为 1)。

给你一个下标从 1 开始的整数数组 flips ,其中 flips[i] 表示对应下标 i 的位将会在第 i 步翻转。

二进制字符串 前缀一致 需满足:在第 i 步之后,在 闭 区间 [1, i] 内的所有位都是 1 ,而其他位都是 0 。

返回二进制字符串在翻转过程中 前缀一致 的次数。

示例 1:

输入:flips = [3,2,4,1,5] 输出:2 解释:二进制字符串最开始是 "00000" 。 执行第 1 步:字符串变为 "00100" ,不属于前缀一致的情况。 执行第 2 步:字符串变为 "01100" ,不属于前缀一致的情况。 执行第 3 步:字符串变为 "01110" ,不属于前缀一致的情况。 执行第 4 步:字符串变为 "11110" ,属于前缀一致的情况。 执行第 5 步:字符串变为 "11111" ,属于前缀一致的情况。 在翻转过程中,前缀一致的次数为 2 ,所以返回 2 。

暴力模拟

我的思路是这样的:当为0开始的时刻,我把light[k]这个灯打开,每一个时刻都要判断当时所有打开的灯左边的灯是否全处于打开状态。所以我们需要用一个布尔数组boolean[] on表示灯的开/关状态:on[i]为真表示i编号的灯已经打开。 只要编号最大的灯左边的所有灯都打开了,就可以保证所有打开的灯左边的灯全是打开状态。 用暴力法解决,每一时刻开灯后,都先确定现在已开的的灯中的最大编号last = Math.max(last, light[i] - 1);。然后查看其左边是否已经所有灯都打开了,如果是则满足题意的时刻的数量加1。

public class Solution {
    public int numTimesAllBlue(int[] light) {
        int n = light.length, result = 0;
        boolean[] isOpen = new boolean[n];
        for (int i = 0; i < n; i++) {
            isOpen[light[i] - 1] = true;
            boolean flag = true;
            for (int j = 0; j <= i; j++) {
                if (!isOpen[j]) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                result++;
            }
        }
        return result;
    }
}

注意

由于每个数字只出现一次,只要当前位置左侧刚好是个全排列就是蓝色的。比如[2,1,4,3,...]前4个数为1~4,就是一个全排列,这种情况一定是蓝色。

而判断是否为全排列,只要看看左侧最大值是否与索引位置一致即可。[2,1,4,...]左侧3个数最大值为4,3!=4因此不全为蓝色。

灯泡开关法

分析思路

当第I个灯被点成蓝灯,那么说明之前的灯全部为蓝灯,那么说明1~I个灯数字全部在数组之前出现过,满足等差数列,那么我们只需要把编号的最大的灯找出来,然后观察到什么地方满足等差数列,就记录一次,全部蓝灯。

public class Solution {
    public int numTimesAllBlue(int[] light) {
        int result = 0, right = 0;
        for (int i = 0; i < light.length; i++) {
            right = Math.max(right, light[i] - 1);
            if (right == i) {
                result++;
            }
        }
        return result;
    }
}
注意点

符合情况的时刻,已亮的灯的编号肯定是符合等差数列的,同时,最大编号的灯也会等于亮灯的次数,所以只需记录灯的最大编号和亮灯次数进行比较即可