Sliding Subarray Beauty
MedPrompt
Given an integer array nums containing n integers, find the beauty of each subarray of size k.
The beauty of a subarray is the xth smallest integer in the subarray if it is negative, or 0 if there are fewer than x negative integers.
Return an integer array containing n - k + 1 integers, which denote the beauty of the subarrays in order from the first index in the array.
-
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
[1, -1, -3]Example 2:
[-1, -2]Example 3:
[-3, 1]
Constraints:
n == nums.length1 <= n <= 1051 <= k <= n1 <= x <= k-50 <= nums[i] <= 50
Approaches
2 approaches with complexity analysis and trade-offs.
This approach iterates through every possible subarray of size k. For each subarray, it identifies all the negative numbers, sorts them, and then finds the x-th smallest one to determine the beauty.
Algorithm
- Initialize an empty result array
ans. - Iterate with a start index
ifrom0ton - k. - For each
i, create a new listnegatives. - Iterate from
j = itoi + k - 1to form the subarray. - If
nums[j]is negative, add it to thenegativeslist. - If
negatives.size() < x, the beauty is0. - Otherwise, sort the
negativeslist and the beauty is the element at indexx - 1. - Add the beauty to the
ansarray. - Return
ans.
Walkthrough
The algorithm proceeds as follows:
- We initialize an answer array
ansof sizen - k + 1to store the beauty of each subarray. - We loop from the first possible starting index
i = 0up ton - k. Eachirepresents the beginning of a new subarray. - For each subarray starting at
iand of lengthk(i.e.,nums[i...i+k-1]), we create a temporary list. - We iterate through this subarray and add all negative numbers to our temporary list.
- After collecting all negative numbers, we sort this list in ascending order.
- We then check the size of the list. If the number of negative elements is less than
x, the beauty is defined as0. - If there are
xor more negative numbers, thex-th smallest negative number is at indexx-1in the sorted list. This value is the beauty. - We store the calculated beauty in the
ansarray at the corresponding index. - After iterating through all possible subarrays, we return the
ansarray.
import java.util.ArrayList;import java.util.Collections;import java.util.List; class Solution { public int[] getSubarrayBeauty(int[] nums, int k, int x) { int n = nums.length; int[] result = new int[n - k + 1]; for (int i = 0; i <= n - k; i++) { // For each subarray, collect negative numbers List<Integer> negatives = new ArrayList<>(); for (int j = i; j < i + k; j++) { if (nums[j] < 0) { negatives.add(nums[j]); } } // If there are fewer than x negative numbers, beauty is 0 if (negatives.size() < x) { result[i] = 0; } else { // Otherwise, sort and find the x-th smallest Collections.sort(negatives); result[i] = negatives.get(x - 1); } } return result; }}Complexity
Time
O(n * k log k) The outer loop runs `n - k + 1` times. In each iteration, we iterate `k` times to form a list of negative numbers. The size of this list can be up to `k`. Sorting this list takes `O(k log k)` time. Thus, the total time complexity is `O((n - k + 1) * k log k)`.
Space
O(n) We use `O(k)` extra space for the list of negative numbers in each iteration. The result array requires `O(n - k + 1)` space, making the total `O(n)`.
Trade-offs
Pros
Simple to understand and implement.
Correctly solves the problem for smaller inputs.
Cons
Inefficient due to re-computation for each subarray.
Sorting within the loop makes it slow, especially for large
k.Does not pass for the given constraints and will likely result in a 'Time Limit Exceeded' error.
This approach uses the sliding window technique combined with a frequency map to efficiently calculate the beauty of each subarray. The key observation is that the range of numbers is small (-50 to 50), which allows us to use a frequency array (acting as a form of counting sort) to track the counts of negative numbers in the current window. This avoids the costly sorting step in each iteration.
Algorithm
- Create a frequency array
freqof size 51 to store counts of negative numbers from -1 to -50. - Initialize the first window: iterate from
i = 0tok-1, and ifnums[i]is negative, incrementfreq[-nums[i]]. - Calculate the beauty for the first window by calling a helper function
findXthSmallestand store it in the result. - The
findXthSmallestfunction iterates fromi = 50down to1(representing numbers -50 to -1). It accumulates the frequencies. When the accumulated count is>= x, it returns-i. If the loop completes, it means there are fewer thanxnegative numbers, so it returns0. - Slide the window from
i = kton-1:- Decrement the frequency of the element leaving the window,
nums[i-k], if it's negative. - Increment the frequency of the element entering the window,
nums[i], if it's negative. - Calculate the beauty for the new window using
findXthSmallestand store it.
- Decrement the frequency of the element leaving the window,
- Return the result array.
Walkthrough
The algorithm works by maintaining a count of each negative number within the current window of size k.
- We use a frequency array,
freq, of size 51. We can map each negative numbernumto an index in this array. A convenient mapping is to usefreq[-num]to store the count ofnum. For example, the count of-1is stored atfreq[1],-2atfreq[2], and so on, up to-50atfreq[50]. - First, we process the initial window of
kelements (nums[0...k-1]). We iterate through these elements and update thefreqarray for any negative numbers encountered. - After populating the frequency map for the first window, we find its beauty. To do this, we iterate through our possible negative numbers from smallest to largest (i.e., from
-50to-1). This corresponds to iterating through ourfreqarray from index50down to1. We accumulate the counts until the total count is at leastx. The number corresponding to the index where this happens is thex-th smallest negative number. If we go through all negative numbers and the total count is less thanx, the beauty is0. - We store this first beauty value in our result array.
- Then, we slide the window one position at a time from left to right. For each step, we update the
freqarray:- Decrement the count for the element that is leaving the window (if it's negative).
- Increment the count for the element that is entering the window (if it's negative).
- After each slide, the
freqarray accurately reflects the counts of negative numbers in the new window. We then repeat the process of finding the beauty using the updatedfreqarray and store it in our result. - This continues until the window has traversed the entire array.
class Solution { public int[] getSubarrayBeauty(int[] nums, int k, int x) { int n = nums.length; int[] result = new int[n - k + 1]; // freq[i] will store the count of number -i. // We only care about negative numbers from -1 to -50. int[] freq = new int[51]; // Initialize the first window for (int i = 0; i < k; i++) { if (nums[i] < 0) { freq[-nums[i]]++; } } result[0] = findXthSmallest(freq, x); // Slide the window for (int i = k; i < n; i++) { // Add the new element if (nums[i] < 0) { freq[-nums[i]]++; } // Remove the old element if (nums[i - k] < 0) { freq[-nums[i - k]]--; } result[i - k + 1] = findXthSmallest(freq, x); } return result; } private int findXthSmallest(int[] freq, int x) { int count = 0; // Iterate from -50 to -1 to find the x-th smallest for (int i = 50; i >= 1; i--) { count += freq[i]; if (count >= x) { return -i; } } // If fewer than x negative numbers exist return 0; }}Complexity
Time
O(n) Initializing the first window takes `O(k)`. The main loop runs `n - k` times. Inside the loop, updating the frequency array takes `O(1)`. The `findXthSmallest` function takes a constant amount of time, `O(C)` where C=50, as it iterates up to 50 times. Therefore, the total time complexity is `O(k + (n - k) * C)`, which simplifies to `O(n)` as C is a small constant.
Space
O(n) The result array requires `O(n - k + 1)` space. The frequency array requires `O(C)` space, where `C` is the range of negative numbers (50). Since `C` is a constant, the total space complexity is dominated by the result array, making it `O(n)`.
Trade-offs
Pros
Highly efficient, with linear time complexity.
Effectively uses the constraint on the range of input values.
Optimal solution for this problem.
Cons
The logic is slightly more complex than the brute-force approach.
This approach is only efficient because the range of values is small and fixed. It would not be suitable if the numbers could be arbitrarily large.
Solutions
Solution
class Solution {public int[] getSubarrayBeauty(int[] nums, int k, int x) { int n = nums.length; int[] cnt = new int[101]; for (int i = 0; i < k; ++i) { ++cnt[nums[i] + 50]; } int[] ans = new int[n - k + 1]; ans[0] = f(cnt, x); for (int i = k, j = 1; i < n; ++i) { ++cnt[nums[i] + 50]; --cnt[nums[i - k] + 50]; ans[j++] = f(cnt, x); } return ans; }private int f(int[] cnt, int x) { int s = 0; for (int i = 0; i < 50; ++i) { s += cnt[i]; if (s >= x) { return i - 50; } } return 0; }}class Solution {public: vector<int> getSubarrayBeauty(vector<int> &nums, int k, int x) { int n = nums.size(); int cnt[101]{}; for (int i = 0; i < k; ++i) { ++cnt[nums[i] + 50]; } vector<int> ans(n - k + 1); auto f = [&](int x) { int s = 0; for (int i = 0; i < 50; ++i) { s += cnt[i]; if (s >= x) { return i - 50; } } return 0; }; ans[0] = f(x); for (int i = k, j = 1; i < n; ++i) { ++cnt[nums[i] + 50]; --cnt[nums[i - k] + 50]; ans[j++] = f(x); } return ans; }};from sortedcontainers import SortedList class Solution : def getSubarrayBeauty ( self , nums : List [ int ], k : int , x : int ) -> List [ int ]: sl = SortedList ( nums [: k ]) ans = [ sl [ x - 1 ] if sl [ x - 1 ] < 0 else 0 ] for i in range ( k , len ( nums )): sl . remove ( nums [ i - k ]) sl . add ( nums [ i ]) ans . append ( sl [ x - 1 ] if sl [ x - 1 ] < 0 else 0 ) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.