当前位置: 首页 > news >正文

滑动窗口题目:统计「优美子数组」

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:统计「优美子数组」

出处:1248. 统计「优美子数组」

难度

5 级

题目描述

要求

给定一个整数数组 nums\texttt{nums}nums 和一个整数 k\texttt{k}k。如果一个连续子数组中恰好有 k\texttt{k}k 个奇数,则该子数组是「优美子数组」。

返回「优美子数组」的数目。

示例

示例 1:

输入:nums=[1,1,2,1,1],k=3\texttt{nums = [1,1,2,1,1], k = 3}nums = [1,1,2,1,1], k = 3
输出:2\texttt{2}2
解释:包含 3\texttt{3}3 个奇数的子数组是 [1,1,2,1]\texttt{[1,1,2,1]}[1,1,2,1][1,2,1,1]\texttt{[1,2,1,1]}[1,2,1,1]

示例 2:

输入:nums=[2,4,6],k=1\texttt{nums = [2,4,6], k = 1}nums = [2,4,6], k = 1
输出:0\texttt{0}0
解释:数列中不包含任何奇数,所以不存在优美子数组。

示例 3:

输入:nums=[2,2,2,1,2,2,1,2,2,2],k=2\texttt{nums = [2,2,2,1,2,2,1,2,2,2], k = 2}nums = [2,2,2,1,2,2,1,2,2,2], k = 2
输出:16\texttt{16}16

数据范围

  • 1≤nums.length≤50000\texttt{1} \le \texttt{nums.length} \le \texttt{50000}1nums.length50000
  • 1≤nums[i]≤105\texttt{1} \le \texttt{nums[i]} \le \texttt{10}^\texttt{5}1nums[i]105
  • 1≤k≤nums.length\texttt{1} \le \texttt{k} \le \texttt{nums.length}1knums.length

解法一

思路和算法

如果数组 nums\textit{nums}nums 的下标范围 [x,y][x, y][x,y] 的子数组中有 kkk 个奇数,且 nums[x]\textit{nums}[x]nums[x]nums[y]\textit{nums}[y]nums[y] 都是奇数,则在不引入新的奇数的情况下,将子数组的开始下标向左移动和将子数组的结束下标向右移动之后,子数组中仍有 kkk 个奇数。假设 nums[x]\textit{nums}[x]nums[x] 左侧有连续 left\textit{left}left 个偶数,nums[y]\textit{nums}[y]nums[y] 右侧有连续 right\textit{right}right 个偶数,则包含 kkk 个奇数且这 kkk 个奇数都在下标范围 [x,y][x, y][x,y] 中的子数组个数是 (left+1)×(right+1)(\textit{left} + 1) \times (\textit{right} + 1)(left+1)×(right+1)

对于长度为 nnn 的数组 nums\textit{nums}nums,假设 nums[−1]\textit{nums}[-1]nums[1]nums[n]\textit{nums}[n]nums[n] 都是奇数,由于 0≤x≤y<n0 \le x \le y < n0xy<n,因此 nums[x]\textit{nums}[x]nums[x] 左侧和 nums[y]\textit{nums}[y]nums[y] 右侧一定有奇数。假设 nums[x]\textit{nums}[x]nums[x] 左侧距离最近的奇数位于下标 x′x'xnums[y]\textit{nums}[y]nums[y] 右侧距离最近的奇数位于下标 y′y'y,则有 left+1=x−x′\textit{left} + 1 = x - x'left+1=xxright+1=y′−y\textit{right} + 1 = y' - yright+1=yy。记 leftCount=x−x′\textit{leftCount} = x - x'leftCount=xxrightCount=y′−y\textit{rightCount} = y' - yrightCount=yy,则包含 kkk 个奇数且这 kkk 个奇数都在下标范围 [x,y][x, y][x,y] 中的子数组个数是 leftCount×rightCount\textit{leftCount} \times \textit{rightCount}leftCount×rightCount

根据上述分析,可以创建一个数组 oddIndices\textit{oddIndices}oddIndices 记录数组 nums\textit{nums}nums 中的所有奇数所在下标,包括下标 −1-11 和下标 nnn。用 oddCount\textit{oddCount}oddCount 表示数组 oddIndices\textit{oddIndices}oddIndices 的长度,当 0<i≤j<oddCount−10 < i \le j < \textit{oddCount} - 10<ij<oddCount1j−i+1=kj - i + 1 = kji+1=k 时,数组 oddIndices\textit{oddIndices}oddIndices 中的下标范围 [i,j][i, j][i,j] 的子数组表示数组 nums\textit{nums}nums 的一个包含 kkk 个奇数的子数组,将 (oddIndices[i]−oddIndices[i−1])×(oddIndices[j+1]−oddIndices[j])(\textit{oddIndices}[i] - \textit{oddIndices}[i - 1]) \times (\textit{oddIndices}[j + 1] - \textit{oddIndices}[j])(oddIndices[i]oddIndices[i1])×(oddIndices[j+1]oddIndices[j]) 加到答案中。

遍历结束之后,即可得到「优美子数组」的数目。

代码

class Solution {public int numberOfSubarrays(int[] nums, int k) {int subarrays = 0;List<Integer> oddIndices = new ArrayList<Integer>();oddIndices.add(-1);int n = nums.length;for (int i = 0; i < n; i++) {if (nums[i] % 2 == 1) {oddIndices.add(i);}}oddIndices.add(n);int oddCount = oddIndices.size();for (int i = 1, j = k; j < oddCount - 1; i++, j++) {int leftCount = oddIndices.get(i) - oddIndices.get(i - 1);int rightCount = oddIndices.get(j + 1) - oddIndices.get(j);subarrays += leftCount * rightCount;}return subarrays;}
}

复杂度分析

  • 时间复杂度:O(n)O(n)O(n),其中 nnn 是数组 nums\textit{nums}nums 的长度。需要创建长度为 O(n)O(n)O(n) 的数组 oddIndices\textit{oddIndices}oddIndices 记录数组 nums\textit{nums}nums 中的所有奇数所在下标,然后使用大小为 kkk 的定长滑动窗口遍历数组 oddIndices\textit{oddIndices}oddIndices 统计「优美子数组」的数目。

  • 空间复杂度:O(n)O(n)O(n),其中 nnn 是数组 nums\textit{nums}nums 的长度。需要创建长度为 O(n)O(n)O(n) 的数组 oddIndices\textit{oddIndices}oddIndices 记录数组 nums\textit{nums}nums 中的所有奇数所在下标。

解法二

思路和算法

解法一创建新数组记录所有奇数所在下标,然后在新数组上使用定长滑动窗口统计「优美子数组」的数目。也可以在数组 nums\textit{nums}nums 上使用变长滑动窗口统计「优美子数组」的数目。

[start,end][\textit{start}, \textit{end}][start,end] 表示滑动窗口,初始时 start=end=0\textit{start} = \textit{end} = 0start=end=0。将滑动窗口的右端点 end\textit{end}end 向右移动,直到滑动窗口中包含 kkk 个奇数(确保 nums[end]\textit{nums}[\textit{end}]nums[end] 是奇数)或者 end\textit{end}end 超出数组下标范围。

当滑动窗口中包含 kkk 个奇数时,需要计算包含 kkk 个奇数且这 kkk 个奇数都在下标范围 [start,end][\textit{start}, \textit{end}][start,end] 中的子数组个数,计算方法如下。

  1. nums[start]\textit{nums}[\textit{start}]nums[start] 是偶数时,将 start\textit{start}start 向右移动,直到 start\textit{start}start 是奇数,然后将 start\textit{start}start 再次向右移动一位,将 start\textit{start}start 的移动次数记为 leftCount\textit{leftCount}leftCount

  2. end\textit{end}end 向右移动一位,当 end\textit{end}end 未超出数组下标范围且 nums[end]\textit{nums}[\textit{end}]nums[end] 是偶数时,将 end\textit{end}end 向右移动,直到 end\textit{end}end 超出数组下标范围或 nums[end]\textit{nums}[\textit{end}]nums[end] 是奇数,将 end\textit{end}end 的移动次数记为 rightCount\textit{rightCount}rightCount

  3. leftCount×rightCount\textit{leftCount} \times \textit{rightCount}leftCount×rightCount 加到答案中。

end\textit{end}end 超出数组下标范围时,遍历结束,此时即可得到「优美子数组」的数目。

上述过程中,每一轮 start\textit{start}start 向右移动时都经过一个奇数,然后停留在该奇数右边的相邻元素,每一轮 end\textit{end}end 向右移动时都是从一个奇数移动到下一个奇数(这里将超出数组下标范围的元素也看成奇数)。因此,每次 start\textit{start}startend\textit{end}end 的移动次数都是数组 nums\textit{nums}nums 中的两个相邻奇数之间的距离,且确保子数组中包含 kkk 个奇数。

代码

class Solution {public int numberOfSubarrays(int[] nums, int k) {int oddCount = 0;int start = 0, end = 0;int n = nums.length;while (end < n) {if (nums[end] % 2 == 1) {oddCount++;if (oddCount == k) {break;}}end++;}int subarrays = 0;while (end < n) {int leftCount = 1, rightCount = 1;while (nums[start] % 2 == 0) {leftCount++;start++;}start++;end++;while (end < n && nums[end] % 2 == 0) {rightCount++;end++;}subarrays += leftCount * rightCount;}return subarrays;}
}

复杂度分析

  • 时间复杂度:O(n)O(n)O(n),其中 nnn 是数组 nums\textit{nums}nums 的长度。滑动窗口的左右端点最多各遍历数组 nums\textit{nums}nums 一次。

  • 空间复杂度:O(1)O(1)O(1)

http://www.dtcms.com/a/405804.html

相关文章:

  • list 迭代器:C++ 容器封装的 “行为统一” 艺术
  • 专题:2025年AI Agent智能体行业洞察报告|附110+份报告PDF、数据仪表盘汇总下载
  • docker部署使用
  • 信息安全基础知识:05物理与环境安全
  • 【双机位A卷】华为OD笔试之【队列】双机位A-篮球游戏【Py/Java/C++/C/JS/Go六种语言】【欧弟算法】全网注释最详细分类最全的华子OD真题题解
  • 考研复习-线性代数-第二章-矩阵
  • wordpress主题样式表绵阳做seo网站公司
  • Answer+cpolar:企业知识共享的远程协作方案
  • 在SSL证书是有效的前提下,依旧显示“资源不安全
  • 鸿蒙NEXT系统Picker全解析:安全高效的用户资源访问之道
  • Gin + JWT 认证机制详解:构建安全的Go Web应用
  • VS要求的.NET 9 SDK需求、安装注意事项及VS版本搭配
  • SSL证书安全的几个问题
  • 4种安全方法:将一加手机联系人传输到电脑
  • 网站建设盐城北安网站建设
  • Guidde:AI驱动的视频文档创建工具
  • 29.Linux防火墙管理
  • 记账本|基于SSM的家庭记账本小程序设计与实现(源码+数据库+文档)
  • DHCP 服务器
  • K8s学习笔记(七) yaml
  • K8S的StorageClass使用节点本地LVM逻辑卷怎么进行PVC扩容
  • 软件开发公司如何通过 UI 设计服务打造差异化竞争力
  • 【源码剖析】5-生产者-RecordAccumulator分析
  • PHP编程基础
  • 单片机 | 基于51单片机的摇摇棒设计全解析
  • 从零开始部署Android环境的Jenkins CI/CD流水线(docker环境,Win系统)
  • HttpSessionBindingListener
  • AndroidEventBus 发布者发布一次订阅者接收到多次问题
  • Unity开发CI/CD工具Jenkins的安装(Windows10)
  • 按键精灵安卓/ios辅助工具,脚本开发新手教程ui界面介绍