3739.统计主要元素子数组数目 II:前缀和+动态规划+枚举维护(问题的多次转换)
【LetMeFly】3739.统计主要元素子数组数目 II:前缀和+动态规划+枚举维护(问题的多次转换)
力扣题目链接:https://leetcode.cn/problems/count-subarrays-with-majority-element-ii/
给你一个整数数组 nums 和一个整数 target。
返回数组 nums 中满足 target 是 主要元素 的 子数组 的数目。
一个子数组的 主要元素 是指该元素在该子数组中出现的次数 严格大于 其长度的 一半 。
子数组 是数组中的一段连续且 非空 的元素序列。
示例 1:
输入: nums = [1,2,2,3], target = 2
输出: 5
解释:
以 target = 2 为主要元素的子数组有:
nums[1..1] = [2]nums[2..2] = [2]nums[1..2] = [2,2]nums[0..2] = [1,2,2]nums[1..3] = [2,2,3]
因此共有 5 个这样的子数组。
示例 2:
输入: nums = [1,1,1,1], target = 1
输出: 10
解释:
所有 10 个子数组都以 1 为主要元素。
示例 3:
输入: nums = [1,2,3], target = 4
输出: 0
解释:
target = 4 完全没有出现在 nums 中。因此,不可能有任何以 4 为主要元素的子数组。故答案为 0。
提示:
1 <= nums.length <= 1051 <= nums[i] <= 1091 <= target <= 109
解题方法:前缀和+动态规划+枚举维护
思路
我们可以先遍历一遍数组,求一个前缀和。令$s[i]$代表从$nums[0]$到$nums$第$i$个元素的子数组中,target比非target多出现的次数。
这样干是为了什么呢?是为了可以通过“判断$s[b]-s[a-1]$是否大于$0$”来确定“$nums$从第$a$个元素到第$b$个元素的子数组是否是主target数组”。
但枚举数组起点终点$a$和$b$的时间复杂度是$O(n^2)$,仍然会超时,我们必须想办法让后一个元素的主target数组个数能结合前一个元素的主target数组在$O(1)$的时间内求出来。
令$f[i]$代表以$nums$中第$i$个元素为右边界的数组中主target数组的个数,那么$f[a+1]$能否由$f[a]$推出来呢?
先来看看$f[a+1]$和$f[a]$的组成中都有哪些不同:
如果第$a+1$个元素是target,那么以第$a$个元素为右边界的
主target数组拼接上第$a+1$个元素组成的新数组仍然是主target数组(这是$f[a+1]$和$f[a]$中相同的部分)。此外,对于“起点$i$满足$s[i]=s[a]$、终点为$a$”的这些子数组,target出现次数和非target出现次数相等,不包含在$f[a]$中。但是由于第$a+1$个元素也是target,所以这些数组拼接上第$a+1$个元素后,target的出现次数会大于非target,即变成了
主target数组(这是$f[a+1]$和$f[a]$中不同的部分)。满足$s[i]=s[a]$的$i$有多少个呢?我们使用一个哈希表统计一下就好了。这样,就有$f[a+1]=f[a]+cnt[s[a]]$。
如果第$a+1$个元素不是target,同理,$f[a+1]=f[a]-cnt[s[a+1]]$,即$f[a]$中target出现次数恰好等于$s[a+1]$的子数组在多了一个非target元素后不再是
主target数组。
时空复杂度分析
- 时间复杂度$O(len(nums))$;
- 空间复杂度$O(len(nums))$,前缀和数组$s$、动态规划数组$f$都可以在$O(1)$的空间里原地滚动,主要空间复杂度来源是记录$s[i]$出现过多少次的哈希表。
AC代码
C++
1 | |
同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
千篇源码题解已开源