[LeetCode解题报告] 995. K 连续位的最小翻转次数

[LeetCode解题报告] 995. K 连续位的最小翻转次数

    • 一、 题目
      • 1. 题目描述
      • 2. 原题链接
    • 二、 解题报告
      • 1. 思路分析
      • 2. 复杂度分析
      • 3. 代码实现
    • 三、 本题小结
    • 四、 参考链接

一、 题目

1. 题目描述

给定一个二进制数组 nums 和一个整数 k

k位翻转 就是从 nums 中选择一个长度为 k子数组 ,同时把子数组中的每一个 0 都改成 1 ,把子数组中的每一个 1 都改成 0

返回数组中不存在 0 所需的最小 k位翻转 次数。如果不可能,则返回 -1 。

子数组 是数组的 连续 部分。

 

示例 1:

输入:nums = [0,1,0], K = 1
输出:2
解释:先翻转 A[0],然后翻转 A[2]。

示例 2:

输入:nums = [1,1,0], K = 2
输出:-1
解释:无论我们怎样翻转大小为 2 的子数组,我们都不能使数组变为 [1,1,1]。

示例 3:

输入:nums = [0,0,0,1,0,1,1,0], K = 3
输出:3
解释:
翻转 A[0],A[1],A[2]: A变成 [1,1,1,1,0,1,1,0]
翻转 A[4],A[5],A[6]: A变成 [1,1,1,1,1,0,0,0]
翻转 A[5],A[6],A[7]: A变成 [1,1,1,1,1,1,1,1]

 

提示:

  • 1 <= nums.length <= 105
  • 1 <= k <= nums.length
Related Topics
  • 位运算
  • 数组
  • 前缀和
  • 滑动窗口

  • 236
  • 0

2. 原题链接

链接: 3995. K 连续位的最小翻转次数

二、 解题报告

1. 思路分析

题意好理解,就是每次翻转必须区间一起翻转。
按顺序向后遇到0就翻转,贪心思想。
  1. 那么如果直接模拟复杂度是O(n×k),会TLE。
  2. 整理步骤发现,是每次询问单点,更新一段,IUPQ,那么可以套树状数组的IUPQ模板。
  3. 过辣!
  4. 但这题还是滑窗比较吊:
  5. 用队列维护窗口内需要翻转的左端点,那么对于每个i,只需要看前边影响到i的窗口里有几个数,即i被翻转了多少次。
    [LeetCode解题报告] 995. K 连续位的最小翻转次数_第1张图片

2. 复杂度分析

最坏时间复杂度O(n)

3. 代码实现

树状数组区间更新单点查询。

class BinIndexTree:
    def __init__(self, size):
        self.size = size
        self.bin_tree = [0 for _ in range(size+5)]
    def add(self,i,v):
        while i<=self.size :
            self.bin_tree[i] += v
            i += self.lowbit(i)
    def update(self,i,v):
        val = v - (self.sum(i)-self.sum(i-1))
        self.add(i,val)
    def sum(self,i):
        s = 0
        while i >= 1:
            s += self.bin_tree[i]
            i -= self.lowbit(i)
        return s
    def lowbit(self,x):
        return x&-x
    def _point_query(self,i):
   		return self.sum(i)
    def _interval_add(self,l,r,v):
   		self.add(l,v)
   		self.add(r+1,-v)

class Solution:
    def minKBitFlips(self, nums: List[int], k: int) -> int:
        """
        1.首先贪心地:向右遍历,对于每个0,以他为左端点翻转k个数。
        2.这样最后遍历结束就是最小,最后失败就-1.
        3.真的翻转就TLE,O(n*k)
        4.不能真翻转的话其实是IUPQ,区间更新单点查询,类似树状数组的IUPQ,其实就是利用差分数组的思想
        5.所以这题正解是差分数组或者滑窗。
        6.滑窗就记录当前下标前k个数翻转了几次,用队列维护一个翻转过的下标窗口即可。队列长度是偶数等于没翻转。
        7.那我用线段树是不是也能做啊
        """
        n = len(nums)
        flip = BinIndexTree(n)
        ans = 0
        for i in range(n-k+1):
            # print(i,nums,flip.bin_tree,nums[i] ^ (flip._point_query(i+1)&1),ans)
            if nums[i] ^ (flip._point_query(i+1)&1) == 1:
                continue
            ans += 1
            # nums[i] = 1
            flip._interval_add(i+1,i+k-1+1,1)

        # print(nums)
        for i in range(n-k+1,n):
            if nums[i] ^ (flip._point_query(i+1)&1) == 0:
                return -1
        return ans

滑窗队列

class Solution:
    def minKBitFlips(self, nums: List[int], k: int) -> int:      
        n = len(nums)
        q = deque()
        ans = 0
        for i in range(n-k+1):
            if q and q[0] <= i-k:
                q.popleft()
            if nums[i] ^ (len(q)&1) == 1:
                continue
            ans += 1
            q.append(i)

        for i in range(n-k+1,n):
            if q and q[0] <= i-k:
                q.popleft()
            if nums[i] ^ (len(q)&1) == 0:
                return -1
        return ans

三、 本题小结

  1. 善用滑窗真的吊。

四、 参考链接

  • 链接: [python刷题模板] 树状数组

你可能感兴趣的:(英雄星球六月集训,今天开刷leetcode,leetcode,算法,贪心算法)