2302. Count Subarrays With Score Less Than K
Problem Description
You are given a positive integer array nums and an integer k. The score of an array is calculated by multiplying the sum of its elements by its length.
For example, if we have an array [1, 2, 3, 4, 5]:
- Sum of elements =
1 + 2 + 3 + 4 + 5 = 15 - Length =
5 - Score =
15 × 5 = 75
Your task is to count how many non-empty subarrays of nums have a score that is strictly less than k.
A subarray is a contiguous sequence of elements from the original array. For instance, if nums = [1, 2, 3, 4], then [2, 3] and [1, 2, 3] are subarrays, but [1, 3] is not (elements are not contiguous).
The function should return the total count of all such subarrays whose score is less than k.
Intuition
When we need to count subarrays with a certain property, a brute force approach would be to check every possible subarray. However, this would be inefficient as there are O(n²) subarrays.
Let's think about the structure of the problem. For a subarray from index i to j, the score is (sum of elements) × (length). If we fix the ending position j, we need to find all valid starting positions i where the score is less than k.
Notice an important property: as we extend a subarray to the left (decreasing the starting index while keeping the ending fixed), both the sum and the length increase. This means the score increases monotonically. In other words, if a subarray of length L ending at position j has a score ≥ k, then all longer subarrays ending at j will also have scores ≥ k.
This monotonic property is perfect for binary search! For each ending position, we can use binary search to find the maximum length l such that the subarray of length l ending at that position has a score less than k. Then all subarrays of length 1, 2, ..., l ending at that position are valid.
To efficiently calculate the sum of any subarray, we can use prefix sums. With prefix sum array s, the sum of elements from index i to j is simply s[j+1] - s[i].
So the approach becomes: for each ending position, binary search for the maximum valid length, and add that length to our answer. The sum of all these lengths gives us the total count of valid subarrays.
Learn more about Binary Search, Prefix Sum and Sliding Window patterns.
Solution Implementation
1from typing import List
2from itertools import accumulate
3
4class Solution:
5 def countSubarrays(self, nums: List[int], k: int) -> int:
6 # Create prefix sum array with initial 0
7 # prefix_sums[i] represents sum of nums[0:i]
8 prefix_sums = list(accumulate(nums, initial=0))
9
10 total_count = 0
11
12 # For each ending position i (1-indexed in prefix_sums)
13 for end_idx in range(1, len(prefix_sums)):
14 # Binary search using first_true_index template
15 # Find first length where score >= k (inverted condition)
16 left, right = 1, end_idx
17 first_true_index = end_idx + 1 # Default: all lengths are valid
18
19 while left <= right:
20 mid = (left + right) // 2
21
22 # Calculate sum of subarray with length mid ending at end_idx-1
23 subarray_sum = prefix_sums[end_idx] - prefix_sums[end_idx - mid]
24
25 # Inverted condition: check if score is TOO LARGE (>= k)
26 if subarray_sum * mid >= k:
27 first_true_index = mid
28 right = mid - 1
29 else:
30 left = mid + 1
31
32 # Maximum valid length = first_true_index - 1
33 # All subarrays of length 1, 2, ..., (first_true_index - 1) are valid
34 total_count += first_true_index - 1
35
36 return total_count
371class Solution {
2 public long countSubarrays(int[] nums, long k) {
3 int n = nums.length;
4
5 // Build prefix sum array where prefixSum[i] = sum of nums[0...i-1]
6 long[] prefixSum = new long[n + 1];
7 for (int i = 0; i < n; ++i) {
8 prefixSum[i + 1] = prefixSum[i] + nums[i];
9 }
10
11 long totalCount = 0;
12
13 // For each ending position i (1-indexed), find all valid subarrays ending at position i
14 for (int endIndex = 1; endIndex <= n; ++endIndex) {
15 // Binary search using first_true_index template
16 // Find first length where score >= k (inverted condition)
17 int left = 1;
18 int right = endIndex;
19 int firstTrueIndex = endIndex + 1; // Default: all lengths are valid
20
21 while (left <= right) {
22 int mid = left + (right - left) / 2;
23
24 // Calculate sum of subarray of length mid ending at endIndex
25 long subarraySum = prefixSum[endIndex] - prefixSum[endIndex - mid];
26
27 // Inverted condition: check if score is TOO LARGE (>= k)
28 if (subarraySum * mid >= k) {
29 firstTrueIndex = mid;
30 right = mid - 1;
31 } else {
32 left = mid + 1;
33 }
34 }
35
36 // Maximum valid length = firstTrueIndex - 1
37 totalCount += firstTrueIndex - 1;
38 }
39
40 return totalCount;
41 }
42}
431class Solution {
2public:
3 long long countSubarrays(vector<int>& nums, long long k) {
4 int n = nums.size();
5
6 // Build prefix sum array where prefixSum[i] = sum of nums[0] to nums[i-1]
7 long long prefixSum[n + 1];
8 prefixSum[0] = 0;
9 for (int i = 0; i < n; ++i) {
10 prefixSum[i + 1] = prefixSum[i] + nums[i];
11 }
12
13 long long result = 0;
14
15 // For each ending position i (1-indexed), find the maximum length of valid subarray
16 for (int i = 1; i <= n; ++i) {
17 // Binary search using first_true_index template
18 // Find first length where score >= k (inverted condition)
19 int left = 1, right = i;
20 int firstTrueIndex = i + 1; // Default: all lengths are valid
21
22 while (left <= right) {
23 int mid = left + (right - left) / 2;
24
25 // Calculate sum of subarray of length 'mid' ending at position i-1
26 long long subarraySum = prefixSum[i] - prefixSum[i - mid];
27
28 // Inverted condition: check if score is TOO LARGE (>= k)
29 if (subarraySum * mid >= k) {
30 firstTrueIndex = mid;
31 right = mid - 1;
32 } else {
33 left = mid + 1;
34 }
35 }
36
37 // Maximum valid length = firstTrueIndex - 1
38 result += firstTrueIndex - 1;
39 }
40
41 return result;
42 }
43};
441/**
2 * Counts the number of subarrays whose score is less than k
3 * Score is defined as the sum of elements multiplied by the length of subarray
4 * @param nums - The input array of numbers
5 * @param k - The threshold value for subarray score
6 * @returns The count of valid subarrays
7 */
8function countSubarrays(nums: number[], k: number): number {
9 const arrayLength: number = nums.length;
10
11 // Build prefix sum array where prefixSum[i] represents sum of first i elements
12 const prefixSum: number[] = Array(arrayLength + 1).fill(0);
13 for (let i = 0; i < arrayLength; ++i) {
14 prefixSum[i + 1] = prefixSum[i] + nums[i];
15 }
16
17 let totalCount: number = 0;
18
19 // For each ending position, find the maximum length of valid subarray
20 for (let endIndex = 1; endIndex <= arrayLength; ++endIndex) {
21 // Binary search using first_true_index template
22 // Find first length where score >= k (inverted condition)
23 let left: number = 1;
24 let right: number = endIndex;
25 let firstTrueIndex: number = endIndex + 1; // Default: all lengths are valid
26
27 while (left <= right) {
28 const mid: number = Math.floor((left + right) / 2);
29
30 // Calculate subarray sum using prefix sum
31 const subarraySum = prefixSum[endIndex] - prefixSum[endIndex - mid];
32
33 // Inverted condition: check if score is TOO LARGE (>= k)
34 if (subarraySum * mid >= k) {
35 firstTrueIndex = mid;
36 right = mid - 1;
37 } else {
38 left = mid + 1;
39 }
40 }
41
42 // Maximum valid length = firstTrueIndex - 1
43 totalCount += firstTrueIndex - 1;
44 }
45
46 return totalCount;
47}
48Solution Approach
The solution uses prefix sum combined with binary search to efficiently count the valid subarrays.
Step 1: Build Prefix Sum Array
First, we create a prefix sum array s where s[i] represents the sum of the first i elements of nums. We initialize it with 0 at index 0 for easier calculation. This allows us to compute the sum of any subarray in O(1) time: the sum from index i to j is s[j+1] - s[i].
s = list(accumulate(nums, initial=0))
Step 2: Binary Search Using first_true_index Template
For each ending position i, we find the maximum valid length where score < k. This is a "last true" pattern, so we invert the condition: find the first length where score >= k.
Using the first_true_index template:
def find_max_valid_length(end_idx):
left, right = 1, end_idx
first_true_index = end_idx + 1 # Default: all lengths are valid
while left <= right:
mid = (left + right) // 2
subarray_sum = prefix_sums[end_idx] - prefix_sums[end_idx - mid]
if subarray_sum * mid >= k: # Inverted: score is TOO LARGE
first_true_index = mid
right = mid - 1
else:
left = mid + 1
# Maximum valid length = first_true_index - 1
return first_true_index - 1
Counting Logic:
first_true_indexgives the first length where score >= k (invalid)first_true_index - 1gives the maximum length where score < k (valid)- All subarrays of length 1, 2, ..., (first_true_index - 1) are valid
Time Complexity: O(n log n) - binary search per position.
Space Complexity: O(n) - prefix sum array.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [2, 1, 4, 3, 5] and k = 10.
Step 1: Build Prefix Sum Array
- Original array:
[2, 1, 4, 3, 5] - Prefix sum
s:[0, 2, 3, 7, 10, 15]s[0] = 0(initial value)s[1] = 0 + 2 = 2s[2] = 2 + 1 = 3s[3] = 3 + 4 = 7s[4] = 7 + 3 = 10s[5] = 10 + 5 = 15
Step 2-4: Process Each Ending Position
Position i = 1 (subarrays ending at index 0 of original array):
- Binary search for max length where score < 10
- Try length 1: sum =
s[1] - s[0] = 2 - 0 = 2, score =2 × 1 = 2 < 10✓ - Maximum valid length = 1
- Valid subarrays:
[2] - Count: 1
Position i = 2 (subarrays ending at index 1):
- Binary search range: length 0 to 2
- Try length 2: sum =
s[2] - s[0] = 3 - 0 = 3, score =3 × 2 = 6 < 10✓ - Maximum valid length = 2
- Valid subarrays:
[1](length 1),[2,1](length 2) - Count: 2
Position i = 3 (subarrays ending at index 2):
- Binary search range: length 0 to 3
- Try length 2: sum =
s[3] - s[1] = 7 - 2 = 5, score =5 × 2 = 10 ≥ 10✗ - Try length 1: sum =
s[3] - s[2] = 7 - 3 = 4, score =4 × 1 = 4 < 10✓ - Maximum valid length = 1
- Valid subarrays:
[4] - Count: 1
Position i = 4 (subarrays ending at index 3):
- Binary search range: length 0 to 4
- Try length 2: sum =
s[4] - s[2] = 10 - 3 = 7, score =7 × 2 = 14 ≥ 10✗ - Try length 1: sum =
s[4] - s[3] = 10 - 7 = 3, score =3 × 1 = 3 < 10✓ - Maximum valid length = 1
- Valid subarrays:
[3] - Count: 1
Position i = 5 (subarrays ending at index 4):
- Binary search range: length 0 to 5
- Try length 3: sum =
s[5] - s[2] = 15 - 3 = 12, score =12 × 3 = 36 ≥ 10✗ - Try length 1: sum =
s[5] - s[4] = 15 - 10 = 5, score =5 × 1 = 5 < 10✓ - Maximum valid length = 1
- Valid subarrays:
[5] - Count: 1
Step 5: Total Count Total = 1 + 2 + 1 + 1 + 1 = 6
The 6 valid subarrays are: [2], [1], [2,1], [4], [3], [5]
Time and Space Complexity
Time Complexity: O(n × log n)
The algorithm uses a prefix sum array and then iterates through each position i from 1 to n (where n is the length of the array). For each position i, it performs a binary search to find the maximum length l of a subarray ending at position i-1 that satisfies the condition (s[i] - s[i - mid]) * mid < k.
- The outer loop runs
ntimes - For each iteration, the binary search performs
O(log i)operations, which in the worst case isO(log n) - Therefore, the overall time complexity is
O(n × log n)
Space Complexity: O(n)
The algorithm creates a prefix sum array s using accumulate with initial=0, which has length n + 1. This is the only additional data structure that scales with the input size. The other variables (ans, i, l, r, mid) use constant space.
- The prefix sum array
srequiresO(n)space - All other variables use
O(1)space - Therefore, the overall space complexity is
O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using Wrong Binary Search Template
This problem finds the maximum valid length where score < k. The correct approach inverts the condition to use the first_true_index template.
Wrong approach:
left, right = 0, end_idx while left < right: mid = (left + right + 1) >> 1 if subarray_sum * mid < k: # Valid left = mid else: right = mid - 1 return left
Correct approach (first_true_index template):
left, right = 1, end_idx first_true_index = end_idx + 1 # Default: all lengths are valid while left <= right: mid = (left + right) // 2 if subarray_sum * mid >= k: # Inverted: find first INVALID length first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index - 1 # Maximum valid length
The key insight is to invert the condition: instead of finding where length IS valid, we find the first length where score >= k (invalid), then return first_true_index - 1.
2. Score Calculation Overflow
The Pitfall:
For large arrays with large values, calculating subarray_sum * length might cause integer overflow in languages with fixed integer sizes. While Python handles arbitrary precision integers automatically, this is a critical issue in languages like Java or C++.
Solution:
- In Python: No action needed due to automatic handling
- In other languages: Use long/int64 types or check for overflow before multiplication
- Alternative approach: Rearrange the inequality to avoid multiplication: instead of
sum * length < k, checksum < k / length(being careful with integer division)
3. Off-by-One Error in Prefix Sum Indexing
The Pitfall:
Confusing the indexing between the original array and the prefix sum array. The prefix sum array has length n+1 where prefix_sums[i] represents the sum of the first i elements of the original array.
Why it happens:
prefix_sums[0] = 0(sum of zero elements)prefix_sums[i] - prefix_sums[j]gives the sum of elements from indexjtoi-1in the original array- It's easy to miscalculate which elements are included in the subarray
Solution:
- Always remember that
prefix_sums[end] - prefix_sums[start]gives the sum ofnums[start:end] - The length of this subarray is
end - start, notend - start + 1 - Draw out small examples to verify the indexing
4. Incorrect Default for first_true_index
The Pitfall:
When no length satisfies score >= k (all lengths are valid), first_true_index should default to end_idx + 1 so that first_true_index - 1 = end_idx gives the correct maximum length.
Wrong: first_true_index = -1 or first_true_index = end_idx
Correct: first_true_index = end_idx + 1
This default ensures that when all subarrays are valid, we correctly count all of them.
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Prefix Sum Given an array of integers arr and a target value find a subarray that sums to the target Return the start and end indices of the subarray Input arr 1 20 3 30 5 4 target 7 Output 1 4 Explanation The subarray 20 3 30 from index 1 inclusive
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Want a Structured Path to Master System Design Too? Don’t Miss This!