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

动态规划思想的延伸:计数模式再升级——巧妙捕捉「优美子数组」

哈喽,各位,我是前端L。

在之前的探险中,“前缀和 + 哈希表”这对黄金搭档,已经帮助我们攻克了“和为K”(LC 560)以及“和是K的倍数”(LC 523)这两大类子数组计数难题。它们的威力在于,能将 O(n²) 的暴力枚举,优雅地降维到 O(n)。

今天,我们将要面对一个它的“变装”版本——“优美子数组”。标准不再是“和”,而是子数组中奇数的个数。但你将惊奇地发现,问题的核心结构,几乎没有改变!同时,我们还将学习一种全新的、基于滑动窗口的计数技巧,进一步拓宽我们的解题思路。

力扣 1248. 統計「优美子数组」

https://leetcode.cn/problems/count-number-of-nice-subarrays/

题目分析: 给定一个整数数组 nums 和一个整数 k,找到连续子数组的个数,使得子数组中恰好包含 k奇数

  • 核心:只关心奇偶性,不关心具体数值。

  • 目标:计数,有多少个这样的子数组。

思路一:“前缀和 + 哈希表”的完美复刻 (O(n) 时间, O(n) 空间)

这道题和“和为K的子数组”(LC 560)简直是“异父异母的亲兄弟”!

1. 思想的“平移”:

  • 在 LC 560 中,我们关心的是元素和。我们用了前缀和 preSum

  • 在本题中,我们关心的是奇数个数。我们只需要用前缀奇数个数 preOddCount 来替换!

2. DP状态定义 (隐式): preOddCount[i] 表示:nums 数组的前 i 个元素 (nums[0...i-1]) 中,奇数的总个数。

3. 核心公式推导: 子数组 nums[l...r] 中奇数的个数 = preOddCount[r+1] - preOddCount[l]。 我们要找的是,有多少对 (l, r) 使得 preOddCount[r+1] - preOddCount[l] == k。 移项得: preOddCount[l] == preOddCount[r+1] - k

4. 算法流程 (与LC 560几乎一致):

  1. 初始化哈希表 oddCountFreq = { {0, 1} },存储 {前缀奇数个数 -> 出现次数}{0, 1} 处理从头开始的子数组。

  2. 初始化 count = 0, currentOddCount = 0

  3. 遍历数组 nums (下标 r): a. 如果 nums[r] 是奇数,currentOddCount++。 b. 计算目标前缀奇数个数 target = currentOddCount - k。 c. 查找哈希表:如果 target 存在于 oddCountFreq 中,count += oddCountFreq[target]。 d. 更新哈希表oddCountFreq[currentOddCount]++

  4. 返回 count

代码实现 (前缀奇数计数 + 哈希表):

#include <vector>
#include <unordered_map>class Solution {
public:int numberOfSubarrays(vector<int>& nums, int k) {int count = 0;int currentOddCount = 0;// 存储 {前缀奇数个数 -> 出现次数}unordered_map<int, int> oddCountFreq;// 初始化:奇数个数为0的前缀出现了1次(空前缀)oddCountFreq[0] = 1;for (int num : nums) {// 更新当前前缀奇数个数if (num % 2 != 0) { // is oddcurrentOddCount++;}// 寻找 target = currentOddCount - kint target = currentOddCount - k;// 如果 target 存在于 map 中,累加其出现次数if (oddCountFreq.count(target)) {count += oddCountFreq[target];}// 将当前的前缀奇数个数加入 mapoddCountFreq[currentOddCount]++;}return count;}
};

思路二:“差分”的智慧——滑动窗口解“恰好K” (O(n) 时间, O(1) 空间)

我们知道,滑动窗口特别擅长处理“最多K”或“最少K”的问题,但直接处理“恰好K”比较棘手。但是,我们可以运用一个极其巧妙的数学思想: 恰好 k 个 = 最多 k 个 - 最多 k-1

count(exactly k) = count(atMost k) - count(atMost k-1)

现在,问题转化为了:如何用滑动窗口,计算一个数组中有多少个子数组,其奇数个数最多k

atMostK(nums, k) 函数的实现:

  1. 初始化 left = 0, oddCountInWindow = 0, result = 0

  2. right 指针遍历数组(扩张窗口): a. 如果 nums[right] 是奇数,oddCountInWindow++。 b. 收缩窗口:只要 oddCountInWindow > k,就不断收缩左边界: i. 如果 nums[left] 是奇数,oddCountInWindow--。 ii. left++。 c. 计数 (核心!):当窗口 [left, right] 满足 oddCountInWindow <= k 后,所有right 结尾,且起点 i 满足 left <= i <= right 的子数组,都满足条件! * 这些子数组的个数是 right - left + 1。 * 将这些的有效子数组计入结果:result += (right - left + 1)。 d. right++

  3. 返回 result

最终答案: 调用 atMostK(nums, k)atMostK(nums, k - 1),然后相减。

代码实现 (滑动窗口 + 差分):

class Solution {
public:int numberOfSubarrays(vector<int>& nums, int k) {// 恰好 k = 最多 k - 最多 k-1return atMostK(nums, k) - atMostK(nums, k - 1);}private:// 计算 nums 中奇数个数 最多 为 k 的子数组个数int atMostK(vector<int>& nums, int k) {int n = nums.size();int left = 0, oddCount = 0, result = 0;for (int right = 0; right < n; ++right) {if (nums[right] % 2 != 0) {oddCount++;}// 当窗口内奇数个数超过 k 时,收缩左边界while (oddCount > k) {if (nums[left] % 2 != 0) {oddCount--;}left++;}// 此时窗口 [left, right] 满足条件// 以 right 结尾的有效子数组个数为 right - left + 1result += (right - left + 1);}return result;}
};

总结:算法模型的“泛化”与“特化”

今天这道题,再次印证了算法世界一个深刻的道理:

许多看似不同的问题,其底层可能共享着同一个数学或逻辑模型。

  • 前缀和 + 哈希表:是解决“子数组和/计数等于(或模等)K”问题的泛化利器。本题只是将“求和”变成了“计数奇数”,模型完美适用。

  • 滑动窗口 + 差分:是解决“恰好K”问题的另一种特化技巧,尤其在空间复杂度上更胜一筹 (O(1))。

掌握识别这些底层模型,并根据问题的细微差别(等于K vs K的倍数 vs 恰好K个奇数),灵活选择或调整武器,是你从“解题”走向“算法设计”的关键一步。

咱们下期见~

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

相关文章:

  • 徐州提供网站建设要多少钱杭州房产网我爱我家官网
  • 鸿蒙NEXT系列之NDK UI监听组件事件
  • 20251025 分治总结
  • CentOS上安装dpkg的方法
  • 【C++ 泛型编程】基于哈希表封装 unordered_set(附完整源码解析)
  • 【Docker】docker部署一个服务
  • 【C++】set map 的使用
  • mormot.net.sock.pas的总体设计分析
  • 上海企业网站制作哪家专业wordpress适合做大型网站吗
  • 建设银行网站怎么看不见余额数字展馆设计
  • 【数组二分查找+数组反转】2022-11-19
  • 【新版发布】标准版PHP v5.6.4正式版,优化部分用户体验
  • Spring Boot3零基础教程,Profile 环境隔离用法,笔记55
  • 【MATLAB例程】二维环境定位,GDOP和CRLB的计算,锚点数=4的情况(附代码下载链接)
  • 英语“近音“易混单词
  • 代码随想录Day62|总结篇
  • 基于VMware和Cent OS的Docker Engine安装与配置
  • 十七、OpenCV中HighGUI模块的介绍和使用
  • 【JVM】详解 编译器原理与优化技术
  • 良好形象的重要性----反思
  • kali抓包流量
  • Python 正则表达式深度解析与实战指南
  • 开源 Linux 服务器与中间件(十二)FRP内网穿透应用
  • 石家庄网站建设王道下拉棒wordpress 类似
  • 基于AT89C52单片机的计算器设计与仿真
  • AI研究-112 DeepSeek-OCR 发展背景 走红原因 新型任务与潜在研究方向 详细分析 附最小运行测试
  • STC32G144K246,高速PWM@240Mhz 运行测试
  • OpenHarmony轻量级内核LiteOS-M技术详解与应用实践
  • hive自定义函数
  • 做新媒体每天必看的网站wordpress exif