Problem: 2653. 滑动子数组的美丽值
给定一个长度为 n
的整数数组 nums
,我们需要计算每个长度为 k
的子数组的美丽值。
美丽值的定义如下:如果子数组中第 x
小的整数是负数,那么美丽值为第 x
小的数,否则美丽值为 0
。
请返回一个包含 n - k + 1
个整数的数组,表示数组中从第一个下标开始,每个长度为 k
的子数组的美丽值。
示例 1:
输入:nums = [1,-1,-3,-2,3], k = 3, x = 2
输出:[-1,-2,-2]
解释:总共有 3 个长度为 k = 3
的子数组。
[1, -1, -3]
,第二小的数是负数 -1
。[-1, -3, -2]
,第二小的数是负数 -2
。[-3, -2, 3]
,第二小的数是负数 -2
。示例 2:
输入:nums = [-1,-2,-3,-4,-5], k = 2, x = 2
输出:[-1,-2,-3,-4]
解释:总共有 4 个长度为 k = 2
的子数组。
[-1, -2]
中第二小的数是负数 -1
。[-2, -3]
中第二小的数是负数 -2
。[-3, -4]
中第二小的数是负数 -3
。[-4, -5]
中第二小的数是负数 -4
。示例 3:
输入:nums = [-3,1,2,-3,0,-3], k = 2, x = 1
输出:[-3,0,-3,-3,-3]
解释:总共有 5 个长度为 k = 2
的子数组。
[-3, 1]
中最小的数是负数 -3
。[1, 2]
中最小的数不是负数,所以美丽值为 0
。[2, -3]
中最小的数是负数 -3
。[-3, 0]
中最小的数是负数 -3
。[0, -3]
中最小的数是负数 -3
。这个问题可以使用滑动窗口来解决,关键在于如何高效地查询第 x
个小的负数。
我们可以使用一个数组 cnt
来记录每个数出现的次数,数组的索引表示数值,数组的值表示该数值出现的次数。为了方便,我们将数组 cnt
的索引偏移了 50
,使得所有数值都在 [0, 100]
的范围内。
具体的算法步骤如下:
n - k + 1
的数组 ans
,用于存放每个子数组的美丽值。nums
,从 0
到 n-1
。
cnt
数组,将当前元素的次数加一。left
为 x
,表示我们需要找到第 x
小的负数。cnt
中的每一个元素,从 0
到 BIAS-1
。
left
减去该元素的次数。left
小于等于 0
,说明已经找到了第 x
小的负数,将当前元素作为答案,并退出循环。cnt
数组,将当前子数组的第一个元素的次数减一。ans
,其中存放着每个子数组的美丽值。class Solution {
public:
vector<int> getSubarrayBeauty(vector<int>& nums, int k, int x) {
const int BIAS = 50;
int cnt[BIAS * 2 + 1]{0}; // 初始化数组,所有元素为0
int n = nums.size();
for (int i = 0; i < k - 1; ++i) {
++cnt[nums[i] + BIAS]; // 更新前k-1个元素的次数
}
vector<int> ans(n - k + 1); // 初始化答案数组
for (int i = k - 1; i < n; i++) {
++cnt[nums[i] + BIAS]; // 更新当前元素的次数
int left = x; // 初始化left为x
// 寻找第x小的负数
for (int j = 0; j < BIAS; ++j) {
left -= cnt[j];
if (left <= 0) {
ans[i - k + 1] = j - BIAS; // 找到答案
break;
}
}
--cnt[nums[i - k + 1] + BIAS]; // 更新当前子数组的第一个元素的次数
}
return ans;
}
};