2237. Count Positions on Street With Required Brightness
Problem Description
You are provided with an integer n
which represents the length of a street on a number line from 0
to n - 1
. There are street lamps along this street, given by a 2D integer array named lights
. Each element of lights
is a pair [position_i, range_i]
, where position_i
denotes the location of a street lamp and range_i
is the range of its light. This range means the lamp can illuminate the street from [max(0, position_i - range_i), min(n - 1, position_i + range_i)]
(both ends included).
The brightness at any position p
on the street is defined as the count of street lamps that cover the position p
. A separate 0-indexed integer array named requirement
specifies the minimum brightness required at each position i
of the street.
The goal is to find and return the count of positions i
on the street (between 0
to n - 1
) where the brightness is at least the specified requirement at i
.
Intuition
The intuition of the solution involves understanding that we need to calculate the brightness at each position on the street and then check if it meets the requirement for that position. A key observation here is that by turning a lamp on, it increases the brightness of all positions within its range; similarly, turning it off would decrease the brightness. This is similar to an interval update in a range query problem.
The challenge then is how to efficiently calculate the brightness at all positions, given that each lamp can affect a potentially broad range and there can be multiple overlaps from different lamps. Directly updating each range for each lamp would lead to a solution that is too slow, as each lamp could affect up to n
positions.
To solve this efficiently, one can use a technique known as prefix sum or cumulative sum. We can increment the brightness at the starting position of a lamp's range and decrement it just after the ending position of the lamp's range. By doing this for all lamps, we would have an array where the value at each position indicates how much the total brightness changes at that point on the street. Accumulating these changes from the beginning to the end will give us the actual brightness at each position.
Once we have the brightness at each position, it's straightforward to compare it with the requirement at each position and count the positions where the brightness is adequate.
The given solution code performs exactly this:
- Initializes an array
d
to store the differences in brightness. - Loops through each lamp and updates the
d
array to include the brightness change at the start and just after the end of each lamp's range. - Utilizes Python's
accumulate
function to calculate the prefix sum of brightness changes, giving the actual brightness at each position. - Counts the number of positions where the actual brightness meets or exceeds the required brightness.
The solution is elegant and efficient, taking advantage of subtle changes in brightness rather than brute-force updating the entire range for every lamp, resulting in a more time-efficient approach.
Learn more about Prefix Sum patterns.
Solution Approach
The solution uses a difference array to efficiently manage brightness updates across the range that each lamp covers. Here's a walkthrough of the steps involved in the solution:
-
Initialize a difference array
d
, which is an auxiliary array that allows us to apply updates to the original array in constant time. In our case, the difference array is oversized to avoid boundary checks later. -
Loop through each lamp's properties given in the
lights
array. For every lamp with parameters[position_i, range_i]
, calculate the starting indexi
and ending indexj
for its illumination. This is done by ensuring that the range does not exceed the boundaries of the street, i.e., between0
andn - 1
. -
Update the difference array
d
by incrementing the value at indexi
by 1 and decrementing the value at indexj + 1
by 1. The increment at indexi
signifies that from this point onwards, the brightness level has increased due to the lamp, while the decrement atj + 1
marks that beyond this point, the influence of the current lamp does not extend, effectively lowering the brightness level. -
Now that we have set up the difference array, we use the
accumulate
function in Python to compute the prefix sum, which essentially applies all the increments and decrements we've added in the difference array and yields the actual brightness level at each index. Theaccumulate
function will perform this operation inO(n)
time across the difference array, thereby generating the brightness levels for the entire street. -
Finally, we loop over the prefix sum result alongside the
requirement
array, comparing values at each position. For every position, we check if the brightness level at that index (obtained from the prefix sum) is greater than or equal to the required brightness level. We count all such occurrences where the condition is satisfied.
The overall time complexity is O(n + m)
, where n
is the length of the street and m
is the number of lamps. This is because we process each lamp to update the difference array once and then scan through the array of length n
to compute the prefix sum and compare it against requirements.
In summary, the code efficiently uses a difference array technique and the power of prefix sum to obtain brightness levels across the street, followed by a simple tally against the specified brightness requirements.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider an example to illustrate the solution approach.
Suppose we are given:
- A street of length
n = 5
, which is from position0
to4
. - An array
lights = [[1, 2], [3, 1]]
, indicating there are two street lamps. The first lamp is at position1
with a range of2
and can illuminate from position0
to3
. The second lamp is at position3
with a range of1
and can illuminate from position2
to4
. - An array
requirement = [1, 2, 1, 1, 2]
specifying the minimum brightness required at each position on the street.
We want to find the count of positions where the brightness is at least the specified requirement.
Let's proceed with the steps:
-
Initialize a difference array
d
of sizen + 1
to handle brightness changes, thusd = [0, 0, 0, 0, 0, 0]
. -
Process the first lamp
[1, 2]
. The starting indexi
ismax(0, 1 - 2) = 0
and the ending indexj
ismin(4, 1 + 2) = 3
. Updated
by incrementingd[i]
and decrementingd[j + 1]
, leading tod = [1, 0, 0, 0, -1, 0]
. -
Process the second lamp
[3, 1]
. The starting indexi
ismax(0, 3 - 1) = 2
and the ending indexj
ismin(4, 3 + 1) = 4
. Updated
by incrementingd[i]
and decrementingd[j + 1]
, which after the update givesd = [1, 0, 1, 0, -1, -1]
. -
Compute the prefix sum with the
accumulate
function to determine the actual brightness at each position. After using accumulate, the brightness levels array becomes[1, 1, 2, 2, 1, 0]
. -
Compare these brightness levels with
requirement
. At positions0
,1
,2
, and3
, the brightness equals or exceeds the requirement, which gives us4
positions meeting the requirement. Position4
has a brightness of1
, which does not meet the requirement of2
.
Hence, the final answer is 4
positions with adequate brightness.
Solution Implementation
1from itertools import accumulate
2from typing import List
3
4class Solution:
5 def meetRequirement(self, n: int, lights: List[List[int]], requirement: List[int]) -> int:
6 # Create a delta array to keep track of the light increments and decrements
7 delta = [0] * (n + 1)
8
9 # Loop through each light's position and range to update the delta array
10 for position, range in lights:
11 # Calculate the left and right effective indices
12 left_effective_index = max(0, position - range)
13 right_effective_index = min(n - 1, position + range)
14
15 # Increase the light intensity at the left index
16 delta[left_effective_index] += 1
17 # Decrease the light intensity right after the right index
18 delta[right_effective_index + 1] -= 1
19
20 # Use accumulate to get the prefix sum of the delta array
21 # which represents the actual lighting at each position
22 actual_lighting = list(accumulate(delta))[:-1]
23
24 # Determine the count of positions meeting the lighting requirement by comparing
25 # actual lighting against the requirement and summing where the condition is true
26 return sum(actual_lighting[i] >= requirement[i] for i in range(n))
27
1class Solution {
2
3 // Method to calculate the number of positions that meet the required brightness
4 public int meetRequirement(int n, int[][] lights, int[] requirement) {
5 // Array 'brightnessChanges' holds the net change in brightness at each position
6 int[] brightnessChanges = new int[100010];
7
8 // Loop through the array of lights to populate the 'brightnessChanges' array
9 for (int[] light : lights) {
10 // Calculate the effective range of light for each light bulb
11 // Make sure the range does not go below 0 or above n-1
12 int start = Math.max(0, light[0] - light[1]);
13 int end = Math.min(n - 1, light[0] + light[1]);
14
15 // Increment brightness at the start position
16 ++brightnessChanges[start];
17 // Decrement brightness just after the end position
18 --brightnessChanges[end + 1];
19 }
20
21 int currentBrightness = 0; // Holds the cumulative brightness at each position
22 int positionsMeetingReq = 0; // Number of positions meeting the requirement
23
24 // Iterate over positions from 0 to n-1
25 for (int i = 0; i < n; ++i) {
26 // Calculate the current brightness by adding the net brightness change at position i
27 currentBrightness += brightnessChanges[i];
28
29 // If current brightness meets or exceeds the requirement at position i, increase count
30 if (currentBrightness >= requirement[i]) {
31 ++positionsMeetingReq;
32 }
33 }
34
35 // Return the total number of positions meeting the brightness requirement
36 return positionsMeetingReq;
37 }
38}
39
1class Solution {
2public:
3 int meetRequirement(int n, vector<vector<int>>& lights, vector<int>& requirement) {
4 // Define a vector to keep track of the differential lighting reach at positions.
5 vector<int> lightingDiff(100010, 0);
6
7 // Iterate through each light source to create the differential array.
8 for (auto& light : lights) {
9 // Calculate the effective range of this light source.
10 int leftBound = max(0, light[0] - light[1]);
11 int rightBound = min(n - 1, light[0] + light[1]);
12
13 // Increment the start of the range.
14 ++lightingDiff[leftBound];
15 // Decrement the position just after the end of the range.
16 --lightingDiff[rightBound + 1];
17 }
18
19 // 'brightnessAccum' will keep track of accumulated brightness as we move along the positions.
20 int brightnessAccum = 0;
21 // 'satisfyingPositions' is the count of positions meeting the requirement.
22 int satisfyingPositions = 0;
23
24 // Iterate through each position and sum up the differential to get the actual brightness.
25 for (int i = 0; i < n; ++i) {
26 brightnessAccum += lightingDiff[i];
27 // If current accumulated brightness meets or exceeds the requirement, increment the count.
28 if (brightnessAccum >= requirement[i]) {
29 ++satisfyingPositions;
30 }
31 }
32
33 // Return the number of positions where lighting requirements are met.
34 return satisfyingPositions;
35 }
36};
37
1// Define a global constant for the maximum number of positions
2const MAX_POSITIONS = 100010;
3
4// 'lightingDiff' is an array to keep track of the differential lighting reach at positions.
5let lightingDiff: number[] = new Array(MAX_POSITIONS).fill(0);
6
7/**
8 * The function 'meetRequirement' calculates the number of positions where the lighting requirements are met.
9 * @param {number} n - Number of positions.
10 * @param {number[][]} lights - Array of light sources, each with position and range.
11 * @param {number[]} requirement - Array of lighting requirements for each position.
12 * @returns {number} - The number of positions that meet the lighting requirements.
13 */
14function meetRequirement(n: number, lights: number[][], requirement: number[]): number {
15 // Iterate through each light source to create the differential array.
16 for (let light of lights) {
17 // Calculate the effective range of this light source.
18 let leftBound = Math.max(0, light[0] - light[1]);
19 let rightBound = Math.min(n - 1, light[0] + light[1]);
20
21 // Increment the start of the range.
22 lightingDiff[leftBound]++;
23 // Decrement the position just after the end of the range.
24 lightingDiff[rightBound + 1]--;
25 }
26
27 // 'brightnessAccum' will keep track of accumulated brightness as we move along the positions.
28 let brightnessAccum = 0;
29 // 'satisfyingPositions' is the count of positions meeting the requirement.
30 let satisfyingPositions = 0;
31
32 // Iterate through each position and sum up the differential to get the actual brightness.
33 for (let i = 0; i < n; i++) {
34 brightnessAccum += lightingDiff[i];
35 // If current accumulated brightness meets or exceeds the requirement, increment the count.
36 if (brightnessAccum >= requirement[i]) {
37 satisfyingPositions++;
38 }
39 }
40
41 // Reset 'lightingDiff' for potential subsequent calls to 'meetRequirement' to ensure correctness
42 lightingDiff.fill(0);
43
44 // Return the number of positions where lighting requirements are met.
45 return satisfyingPositions;
46}
47
Time and Space Complexity
Time Complexity
The main operations within the meetRequirement
function are as follows:
- Iterating over the
lights
list to populate the differences in thed
array. This has a time complexity ofO(m)
, wherem
is the length of thelights
list. - Using the
itertools.accumulate
function to compute the prefix sums of the arrayd
. This has a time complexity ofO(n)
sinceaccumulate
will sum across then
elements of thed
array. - Zipping the accumulated sums with the
requirement
array and iterating over it to count the number of positions meeting the requirement. The zipping has a time complexity ofO(n)
since it operates on two arrays ofn
elements each. The sum operation also takesO(n)
.
Therefore, the time complexity of the complete function is O(m + n)
because O(m)
for the iterations over the lights
list and O(n)
for the operations involving the d
array are independent and do not nest.
Space Complexity
For space complexity, the main data structures that are used in the function include:
- The difference array
d
, which has a fixed maximum size due to its initialization. This maximum size (100010) gives us a space complexity ofO(1)
since it does not scale with the input sizen
. - The use of
itertools.accumulate
which generates an iterator. The space taken by this iterator is proportional to the number of elements ind
, leading to a space complexity ofO(n)
. - The intermediate tuples created during the zipping process are not stored; they're generated on-the-fly during iteration, making their additional space impact negligible.
In conclusion, the space complexity of the function is O(n)
due to the storage requirements of the difference array d
as it scales linearly with the input size n
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which algorithm should you use to find a node that is close to the root of the tree?
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
LeetCode 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 we
Recursion 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 algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!