2237. Count Positions on Street With Required Brightness
Problem Description
You have a straight street represented as a number line from 0 to n - 1. There are street lamps placed at various positions along this street.
Each street lamp is described by two values:
position_i: where the lamp is located on the streetrange_i: how far the lamp's light reaches in both directions
A lamp at position position_i with range range_i illuminates all positions from max(0, position_i - range_i) to min(n - 1, position_i + range_i) inclusive. This means the lamp lights up positions within its range but doesn't go beyond the street boundaries (0 and n-1).
The brightness of any position on the street equals the total number of lamps that illuminate that position. For example, if 3 different lamps illuminate position 5, then position 5 has a brightness of 3.
You're given an array requirement where requirement[i] specifies the minimum brightness needed at position i.
Your task is to count how many positions on the street meet or exceed their brightness requirement. In other words, count the positions where the actual brightness is greater than or equal to the required brightness.
Example: If a position needs brightness of 2 (requirement) and 3 lamps illuminate it (actual brightness = 3), this position meets the requirement and should be counted.
The function should return the total number of positions between 0 and n - 1 that satisfy their brightness requirements.
Intuition
The key insight is that we need to efficiently track how many lamps illuminate each position on the street. A naive approach would be to iterate through each lamp and mark every position it illuminates, but this could be inefficient when lamps have large ranges.
Instead, we can use a difference array technique. The idea is that when a lamp illuminates a continuous range of positions, rather than updating each position individually, we only mark the boundaries of the range.
Think of it like this: when a lamp starts illuminating at position i, we mark "+1 lamp starts here". When the lamp stops illuminating after position j, we mark "-1 lamp ends here".
For example, if a lamp illuminates positions [2, 5], we put:
+1at position 2 (a lamp starts illuminating here)-1at position 6 (a lamp stops illuminating after position 5)
When we later compute the prefix sum of these markers, the running sum at any position tells us exactly how many lamps are illuminating that position. This works because:
- Each
+1we encounter means a new lamp range has started - Each
-1we encounter means a lamp range has ended - The cumulative sum gives us the total number of active lamp ranges at each position
This transforms our problem from "update many positions for each lamp" to "update only 2 positions for each lamp", followed by a single pass to compute prefix sums. Once we have the brightness at each position through the prefix sum, we simply compare it with the requirement array to count positions that meet their minimum brightness requirement.
Learn more about Prefix Sum patterns.
Solution Implementation
1from typing import List
2from itertools import accumulate
3
4class Solution:
5 def meetRequirement(
6 self, n: int, lights: List[List[int]], requirement: List[int]
7 ) -> int:
8 # Initialize difference array for range updates
9 # Extra element at index n for handling range end boundaries
10 diff_array = [0] * (n + 1)
11
12 # Process each light source
13 for position, range_radius in lights:
14 # Calculate the leftmost and rightmost positions affected by this light
15 # Ensure boundaries stay within [0, n-1]
16 left_bound = max(0, position - range_radius)
17 right_bound = min(n - 1, position + range_radius)
18
19 # Apply range update using difference array technique
20 # Increment at start of range, decrement after end of range
21 diff_array[left_bound] += 1
22 diff_array[right_bound + 1] -= 1
23
24 # Calculate actual brightness at each position using prefix sum
25 # Then count positions where brightness meets or exceeds requirement
26 brightness_values = accumulate(diff_array)
27 positions_meeting_requirement = sum(
28 brightness >= required
29 for brightness, required in zip(brightness_values, requirement)
30 )
31
32 return positions_meeting_requirement
331class Solution {
2 public int meetRequirement(int n, int[][] lights, int[] requirement) {
3 // Create a difference array to track brightness changes
4 // Size is n+1 to handle the boundary case when decrementing at position n
5 int[] differenceArray = new int[n + 1];
6
7 // Process each light source and mark its coverage range
8 for (int[] light : lights) {
9 // Calculate the leftmost position affected by this light
10 // light[0] is position, light[1] is range
11 int leftBoundary = Math.max(0, light[0] - light[1]);
12
13 // Calculate the rightmost position affected by this light
14 int rightBoundary = Math.min(n - 1, light[0] + light[1]);
15
16 // Mark the start of the light's coverage with increment
17 differenceArray[leftBoundary]++;
18
19 // Mark the end of the light's coverage with decrement
20 differenceArray[rightBoundary + 1]--;
21 }
22
23 // Calculate actual brightness at each position using prefix sum
24 int currentBrightness = 0;
25 int positionsMeetingRequirement = 0;
26
27 // Iterate through each position to check if it meets the requirement
28 for (int position = 0; position < n; position++) {
29 // Update current brightness by applying the difference
30 currentBrightness += differenceArray[position];
31
32 // Check if current position meets its brightness requirement
33 if (currentBrightness >= requirement[position]) {
34 positionsMeetingRequirement++;
35 }
36 }
37
38 return positionsMeetingRequirement;
39 }
40}
411class Solution {
2public:
3 int meetRequirement(int n, vector<vector<int>>& lights, vector<int>& requirement) {
4 // Use difference array to efficiently track brightness changes
5 // diff[i] represents the change in brightness at position i
6 vector<int> diff(n + 1);
7
8 // Process each light source
9 for (const auto& light : lights) {
10 // Calculate the leftmost position affected by this light
11 // light[0] is the position, light[1] is the range
12 int leftBound = max(0, light[0] - light[1]);
13
14 // Calculate the rightmost position affected by this light
15 int rightBound = min(n - 1, light[0] + light[1]);
16
17 // Mark the start and end of the light's effect using difference array
18 ++diff[leftBound]; // Brightness increases starting from leftBound
19 --diff[rightBound + 1]; // Brightness decreases after rightBound
20 }
21
22 // Calculate actual brightness at each position and count positions meeting requirement
23 int currentBrightness = 0;
24 int positionsMeetingRequirement = 0;
25
26 for (int i = 0; i < n; ++i) {
27 // Update current brightness by accumulating the difference
28 currentBrightness += diff[i];
29
30 // Check if current position meets its brightness requirement
31 if (currentBrightness >= requirement[i]) {
32 ++positionsMeetingRequirement;
33 }
34 }
35
36 return positionsMeetingRequirement;
37 }
38};
391/**
2 * Counts the number of positions that meet the brightness requirement
3 * @param n - The number of positions on the street (0 to n-1)
4 * @param lights - Array of [position, radius] pairs representing street lights
5 * @param requirement - Array of minimum brightness requirements for each position
6 * @returns The count of positions that meet their brightness requirement
7 */
8function meetRequirement(n: number, lights: number[][], requirement: number[]): number {
9 // Initialize difference array to track brightness changes
10 // Using n+1 size to handle boundary updates safely
11 const brightnessChanges: number[] = Array(n + 1).fill(0);
12
13 // Process each light and mark its coverage range using difference array technique
14 for (const [position, radius] of lights) {
15 // Calculate the leftmost position this light can illuminate (bounded by 0)
16 const leftBoundary: number = Math.max(0, position - radius);
17
18 // Calculate the rightmost position this light can illuminate (bounded by n-1)
19 const rightBoundary: number = Math.min(n - 1, position + radius);
20
21 // Mark the start of illumination range
22 brightnessChanges[leftBoundary]++;
23
24 // Mark the end of illumination range (position after the last illuminated point)
25 brightnessChanges[rightBoundary + 1]--;
26 }
27
28 // Count positions that meet their brightness requirement
29 let positionsMeetingRequirement: number = 0;
30 let currentBrightness: number = 0;
31
32 // Iterate through each position and calculate actual brightness using prefix sum
33 for (let position: number = 0; position < n; position++) {
34 // Update current brightness by applying the change at this position
35 currentBrightness += brightnessChanges[position];
36
37 // Check if current position meets its brightness requirement
38 if (currentBrightness >= requirement[position]) {
39 positionsMeetingRequirement++;
40 }
41 }
42
43 return positionsMeetingRequirement;
44}
45Solution Approach
We implement the solution using a difference array technique:
-
Initialize the difference array: Create an array
dof sizen + 1(one extra position to handle the boundary). Initialize all values to 0. -
Process each street lamp: For each lamp at position
pwith ranger:- Calculate the left boundary of illumination:
i = max(0, p - r)(ensuring we don't go below position 0) - Calculate the right boundary of illumination:
j = min(n - 1, p + r)(ensuring we don't exceed position n-1) - Mark the start of illumination:
d[i] += 1 - Mark the end of illumination:
d[j + 1] -= 1
- Calculate the left boundary of illumination:
-
Compute actual brightness using prefix sum: Apply the
accumulatefunction to the difference arrayd. This gives us the actual brightness at each position. The prefix sum works because:- Starting from position 0, we accumulate the difference values
- Each
+1increases the running count (a lamp range starts) - Each
-1decreases the running count (a lamp range ends) - The accumulated value at position
iequals the number of lamps illuminating that position
-
Count positions meeting requirements: Compare the actual brightness (from the prefix sum) with the required brightness at each position. Use
zipto pair up the accumulated brightness values with the requirements, and count how many positions havebrightness >= requirement.
The final expression sum(s >= r for s, r in zip(accumulate(d), requirement)) elegantly combines steps 3 and 4:
accumulate(d)generates the brightness valueszippairs each brightness with its corresponding requirement- The generator expression checks if each position meets its requirement
sumcounts the total number of positions that satisfy the condition
This approach has a time complexity of O(m + n) where m is the number of lamps and n is the street length, which is much more efficient than the naive O(m * n) approach.
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 a concrete example to illustrate the solution approach.
Given:
- Street length:
n = 7(positions 0 to 6) - Street lamps:
[[2, 2], [5, 1]]- Lamp 1: position 2, range 2 (illuminates positions 0-4)
- Lamp 2: position 5, range 1 (illuminates positions 4-6)
- Requirements:
[1, 0, 2, 0, 1, 1, 0]
Step 1: Initialize difference array
Create array d of size 8 (n+1):
d = [0, 0, 0, 0, 0, 0, 0, 0]
Step 2: Process each lamp
Lamp 1 (position=2, range=2):
- Left boundary: max(0, 2-2) = 0
- Right boundary: min(6, 2+2) = 4
- Mark boundaries: d[0] += 1, d[5] -= 1
d = [1, 0, 0, 0, 0, -1, 0, 0]
Lamp 2 (position=5, range=1):
- Left boundary: max(0, 5-1) = 4
- Right boundary: min(6, 5+1) = 6
- Mark boundaries: d[4] += 1, d[7] -= 1
d = [1, 0, 0, 0, 1, -1, 0, -1]
Step 3: Compute brightness using prefix sum Apply accumulate to get actual brightness at each position:
Position: 0 1 2 3 4 5 6 d values: 1 0 0 0 1 -1 0 Brightness: 1 1 1 1 2 1 1
The prefix sum works as:
- Position 0: 1 (one lamp range starts)
- Position 1: 1 (no change, still in same lamp range)
- Position 2-3: 1 (continuing same lamp range)
- Position 4: 2 (second lamp range starts, now two lamps overlap)
- Position 5: 1 (first lamp range ends, only second lamp remains)
- Position 6: 1 (second lamp continues)
Step 4: Count positions meeting requirements Compare brightness with requirements:
Position: 0 1 2 3 4 5 6 Brightness: 1 1 1 1 2 1 1 Requirement: 1 0 2 0 1 1 0 Meets req?: ✓ ✓ ✗ ✓ ✓ ✓ ✓
- Position 0: 1 >= 1 ✓
- Position 1: 1 >= 0 ✓
- Position 2: 1 < 2 ✗
- Position 3: 1 >= 0 ✓
- Position 4: 2 >= 1 ✓
- Position 5: 1 >= 1 ✓
- Position 6: 1 >= 0 ✓
Result: 6 positions meet their brightness requirements.
Time and Space Complexity
The time complexity is O(m + n), where m is the number of lights and n is the number of positions. The algorithm iterates through all m lights to build the difference array (taking O(m) time), then uses accumulate to compute prefix sums over n positions and compares them with requirements (taking O(n) time). Since the reference answer simplifies this to O(n), it likely assumes m ≤ n in the worst case, making the overall complexity O(n).
The space complexity is O(n). The algorithm creates a difference array d of size n + 1 to track the changes in light coverage. The accumulate function generates values on-the-fly during iteration, and the final sum operation doesn't require additional space proportional to n. Therefore, the dominant space usage comes from the difference array, resulting in O(n) space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Off-by-One Error in Range Boundaries
The Pitfall: A common mistake is incorrectly handling the range boundaries when marking the difference array. Developers might write:
# INCORRECT - marks wrong end boundary diff_array[right_bound] -= 1 # Wrong!
Why it happens: The difference array technique requires marking the position after the range ends. Since a lamp illuminates positions from left_bound to right_bound inclusive, we need to decrement at right_bound + 1 to indicate the light stops affecting positions beyond right_bound.
Solution: Always mark the decrement at right_bound + 1:
# CORRECT diff_array[right_bound + 1] -= 1
2. Insufficient Array Size
The Pitfall: Creating a difference array of size n instead of n + 1:
# INCORRECT - array too small diff_array = [0] * n # IndexError when right_bound = n-1
Why it happens: When right_bound = n - 1, we need to access diff_array[n] to mark the range end. Without the extra element, this causes an IndexError.
Solution: Always allocate n + 1 elements:
# CORRECT diff_array = [0] * (n + 1)
3. Misunderstanding Accumulate Output Length
The Pitfall: Assuming accumulate(diff_array) returns exactly n elements when the input has n + 1 elements.
Why it happens: accumulate returns an iterator with the same number of elements as the input. Since diff_array has n + 1 elements, accumulate(diff_array) also yields n + 1 values, but we only need the first n for comparison with requirement.
Solution: The zip function naturally handles this by stopping at the shorter sequence (requirement has n elements), so the code works correctly. However, for clarity, you could explicitly slice:
# More explicit version
brightness_values = list(accumulate(diff_array))[:n]
positions_meeting_requirement = sum(
brightness >= required
for brightness, required in zip(brightness_values, requirement)
)
4. Integer Overflow in Other Languages
The Pitfall: While not an issue in Python, implementing this in languages like C++ or Java requires careful consideration of integer overflow when many lamps overlap at the same position.
Solution: In Python, integers have arbitrary precision, so this isn't a concern. In other languages, use appropriate data types (e.g., long in Java, long long in C++) if the number of overlapping lamps could be large.
In a binary min heap, the maximum element can be found in:
Recommended Readings
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!