【单调向量 单调栈】3676. 碗子数组的数目|1848
单调向量
C++单调栈
3676. 碗子数组的数目
给你一个整数数组 nums,包含 互不相同 的元素。
nums 的一个子数组 nums[l…r] 被称为 碗(bowl),如果它满足以下条件:
子数组的长度至少为 3。也就是说,r - l + 1 >= 3。
其两端元素的 最小值 严格大于 中间所有元素的 最大值。也就是说,min(nums[l], nums[r]) > max(nums[l + 1], …, nums[r - 1])。
返回 nums 中 碗 子数组的数量。
子数组 是数组中连续的元素序列。
示例 1:
输入: nums = [2,5,3,1,4]
输出: 2
解释:
碗子数组是 [3, 1, 4] 和 [5, 3, 1, 4]。
[3, 1, 4] 是一个碗,因为 min(3, 4) = 3 > max(1) = 1。
[5, 3, 1, 4] 是一个碗,因为 min(5, 4) = 4 > max(3, 1) = 3。
示例 2:
输入: nums = [5,1,2,3,4]
输出: 3
解释:
碗子数组是 [5, 1, 2]、[5, 1, 2, 3] 和 [5, 1, 2, 3, 4]。
示例 3:
输入: nums = [1000000000,999999999,999999998]
输出: 0
解释:
没有子数组是碗。
提示:
3 < = n u m s . l e n g t h < = 1 0 5 3 <= nums.length <= 10^5 3<=nums.length<=105
1 < = n u m s [ i ] < = 1 0 9 1 <= nums[i] <= 10^9 1<=nums[i]<=109
nums 由不同的元素组成。
单调向量
r 从2到大枚举,v记录所有可能的left。
性质一:如果nums[i]< nums[r-1],则任意r1 \ge r,[i…r1]都不是碗子数组。故nums[i]可以永久被删除。
性质二:按性质一删除后,v降序。
性质三:v[i]对应k1,v[i+1]对应k2,则 ∀ k 1 ≤ k ≤ k 2 \forall k1\le k \le k2 ∀k1≤k≤k2,nums[k] < nums[k2] < nums[k1]。
nums[r-1]按性质一入向量。如果nums[r] < nums[r-1],则 ∀ l e f t \forall left ∀left,nums[left ⋯ r \cdots r ⋯r]都不是碗子数组。否则:
it 是v中指向第一个小于nums[r]的迭代器。如果it不是起始迭代器,则–it。
v.end()-it 就是 nums[left ⋯ r \cdots r ⋯r]都是碗子数组的数量。
核心代码
class Solution {public:long long bowlSubarrays(vector<int>& nums) {vector<int> v = { nums[0] };long long ans = 0; for(int i = 2; i < nums.size();i++){while (v.size() && ( v.back()< nums[i-1])){v.pop_back();}if (nums[i - 1] < nums[i]) {auto it = lower_bound(v.begin(), v.end(), nums[i],greater<>());ans += v.end()-it;ans += (v.begin() != it);}v.emplace_back(nums[i-1]);}return ans;}};
单元测试
vector<int> nums;TEST_METHOD(TestMethod11){nums = { 2,5,3,1,4 };auto res = Solution().bowlSubarrays(nums);AssertEx(2LL, res);}TEST_METHOD(TestMethod12){nums = { 5,1,2,3,4 };auto res = Solution().bowlSubarrays(nums);AssertEx(3LL, res);}TEST_METHOD(TestMethod13){nums = { 1000000000,999999999,999999998 };auto res = Solution().bowlSubarrays(nums);AssertEx(0LL, res);}
单调栈
进一步优化。
性质四:如果nums[r]淘汰nums[left],则 ∀ k , n u m s [ l e f t ] > n u m s [ k ] \forall k,nums[left]>nums[k] ∀k,nums[left]>nums[k],否则nums[k]会淘汰nums[left];nums[r]>nums[k],否则无法淘汰。
如果r - left+1 g e > 3 ge>3 ge>3 则是解。
如果v.back()存在,则nums[v.back() ⋯ \cdots ⋯r]也是碗子数组,且下标差 ≥ 3 \ge 3 ≥3,也是碗子数组。
空间复杂度、时间复杂度:都是O(n)。
核心代码
class Solution {public:long long bowlSubarrays(vector<int>& nums) {stack<int> sta;long long ans = 0;for (int i = 0; i < nums.size();i++) {while (sta.size() && (nums[sta.top()] < nums[i])){ans += (i - sta.top() + 1 >= 3);sta.pop();}if (sta.size()){ans += (i - sta.top() + 1 >= 3);}sta.emplace(i);}return ans;}};
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
员工说:技术至上,老板不信;投资人的代表说:技术至上,老板会信。 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。