ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Subarray With Elements Greater Than Varying Threshold

2022-08-29 20:32:36  阅读:216  来源: 互联网

标签:cnt Elements Greater idx nums int fa Varying find


Subarray With Elements Greater Than Varying Threshold

You are given an integer array $nums$ and an integer $threshold$.

Find any subarray of $nums$ of length $k$ such that every element in the subarray is greater than $threshold / k$.

Return the size of any such subarray. If there is no such subarray, return $-1$.

A subarray is a contiguous non-empty sequence of elements within an array.

Example 1:

Input: nums = [1,3,4,3,1], threshold = 6
Output: 3
Explanation: The subarray [3,4,3] has a size of 3, and every element is greater than 6 / 3 = 2.
Note that this is the only valid subarray.

Example 2:

Input: nums = [6,5,6,5,8], threshold = 7
Output: 1
Explanation: The subarray [8] has a size of 1, and 8 > 7 / 1 = 7. So 1 is returned.
Note that the subarray [6,5] has a size of 2, and every element is greater than 7 / 2 = 3.5. 
Similarly, the subarrays [6,5,6], [6,5,6,5], [6,5,6,5,8] also satisfy the given conditions.
Therefore, 2, 3, 4, or 5 may also be returned.

Constraints:

$1 \leq nums.length \leq {10}^{5}$
$1 \leq nums[i], threshold \leq {10}^9$

 

解题思路

  因为$k$的取值范围是$1 \sim n$,因此可以枚举$k$。这里不可以用二分,因为不具有二段性。

  从小到大枚举$k$,那么随着$k$的增大,$\frac{T}{k}$会逐渐减小,因此满足要求的数会逐渐增多。在增大$k$的过程中,如果发现某个数满足$nums[i] > \frac{T}{k}$,那么就把它标记出来,$i$为这个数的下标,这里的标记是指在数轴$i$这个位置打个标记,表示这个位置上有数,一开始这个数轴是空的。因此每次增加$k$的值,就逐渐会有满足要求的数加进数轴,数轴上加进来的数越来越多。每加入一个数,就判断与这个数相连的连续一段数的个数是否大于等于$k$就好了。

  因此我们需要找个数据结构来实现我们的操作。一开始区间(数轴)是空的,每次往里面添加一个元素,添加元素之后维护包括当前元素所在的连续被标记过的区间长度(如上图),看一下区间的长度是多少。我们可以用并查集来实现,不过这个并查集与一般的并查集不一样。

  参考修改数组。一开始所有元素都没有被标记,因此并查集初始化时每个元素都指向自己。如果某个元素被标记了,那么这个元素就指向它的下一个位置的元素。因此每个集合中除了根节点(代表元素)外,其余的元素都是被标记过的(根元素指向自己因此没被标记)。还需要维护每个集合的大小,因此每个集合中被标记的元素个数就是当前集合的大小减去$1$。

  另外,因为我们是从小到大去枚举$k$,因此为了快速知道哪些数满足$nums[i] > \frac{T}{k}$(元素都是从大到小添加进来的),我们先对数组进行降序排序(数值和对应的下标进行排序,实际上我们只用到排序后这个数在原数组中的下标是多少)。

  AC代码如下:

 1 class Solution {
 2 public:
 3     vector<int> fa, idx, cnt;
 4 
 5     int find(int x) {
 6         return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
 7     }
 8 
 9     int validSubarraySize(vector<int>& nums, int threshold) {
10         int n = nums.size();
11         for (int i = 0; i <= n; i++) {
12             fa.push_back(i);
13             idx.push_back(i);   // idx记录每个数在原始数组中的下标
14             cnt.push_back(1);
15         }
16 
17         idx.pop_back(); // 把下标n弹出
18         sort(idx.begin(), idx.end(), [&](int a, int b) {    // 根据原始数组中每个数的数值从大到小排序,最后idx就得到排序后每个数在原始数组中的下标位置
19             return nums[a] > nums[b];
20         });
21 
22         for (int k = 1, i = 0; k <= n; k++) {
23             while (i < n && nums[idx[i]] > threshold / k) {
24                 // idx[i]表示元素数组的的下标,idx[i]+1表示这个元素在原始数组的下标的下一个位置
25                 cnt[find(idx[i] + 1)] += cnt[idx[i]];
26                 fa[idx[i]] = find(idx[i] + 1);  // 当前元素指向下一个位置的元素
27                 if (cnt[find(idx[i])] - 1 >= k) return k;
28                 i++;
29             }
30         }
31 
32         return -1;
33     }
34 };

  其实不用这种并查集也是可以的。本质上是往区间加入一个元素,然后对这个元素左右两边的区间进行合并(如果可以合并的话),然后再求合并后的新的区间的长度,也就是动态维护连续的区间。用普通并查集也是可以做的。

  AC代码如下:

 1 class Solution {
 2 public:
 3     vector<int> fa, idx, cnt;
 4 
 5     int find(int x) {
 6         return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
 7     }
 8 
 9     int validSubarraySize(vector<int>& nums, int threshold) {
10         int n = nums.size();
11         for (int i = 0; i < n; i++) {
12             fa.push_back(i);
13             idx.push_back(i);
14             cnt.push_back(0);   // 这里初始化为0是为了方便表示当前元这个素还没有被标记
15         }
16 
17         sort(idx.begin(), idx.end(), [&](int a, int b) {
18             return nums[a] > nums[b];
19         });
20 
21         for (int k = 1, i = 0; k <= n; k++) {
22             while (i < n && nums[idx[i]] > threshold / k) {
23                 cnt[idx[i]] = 1;    // 当前元素被标记,赋值为1
24                 if (idx[i] - 1 >= 0 && cnt[find(idx[i] - 1)]) { // 左边的元素所在区间已被标记,就把左边的集合与新元素合并
25                     cnt[idx[i]] += cnt[find(idx[i] - 1)];
26                     fa[find(idx[i] - 1)] = idx[i];
27                 }
28                 if (idx[i] + 1 < n && cnt[find(idx[i] + 1)]) {  // 右边的元素所在区间已被标记,就把右边的集合与新元素合并
29                     cnt[idx[i]] += cnt[find(idx[i] + 1)];
30                     fa[find(idx[i] + 1)] = idx[i];
31                 }
32                 if (cnt[idx[i]] >= k) return k;
33                 i++;
34             }
35         }
36 
37         return -1;
38     }
39 };

 

参考资料

  出题人表示,就是要深夜搞你们心态!力扣第82场双周赛:https://www.bilibili.com/video/BV1s94y197XG

  C++匿名函数:https://www.cnblogs.com/Brickert/p/13164291.html

标签:cnt,Elements,Greater,idx,nums,int,fa,Varying,find
来源: https://www.cnblogs.com/onlyblues/p/16637247.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有