1052. Grumpy Bookstore Owner
Problem Description
You are given a bookstore that is open for n
minutes. During each minute, a certain number of customers enter and leave the store.
You have two arrays:
customers[i]
: the number of customers entering at minutei
(they all leave after that minute ends)grumpy[i]
: a binary value where1
means the owner is grumpy at minutei
, and0
means they're not grumpy
When the owner is grumpy, customers entering during that minute are not satisfied. When the owner is not grumpy, customers are satisfied.
The owner has a special technique: they can force themselves to not be grumpy for exactly minutes
consecutive minutes, but this technique can only be used once during the entire day.
Your task is to find the maximum number of satisfied customers possible by strategically using the technique at the optimal time.
For example, if customers = [1,0,1,2,1,1,7,5]
, grumpy = [0,1,0,1,0,1,0,1]
, and minutes = 3
:
- Without using the technique, satisfied customers =
1
(minute 0) +1
(minute 2) +1
(minute 4) +7
(minute 6) =10
- If we use the technique from minute 5 to 7, we can additionally satisfy
1
(minute 5) +5
(minute 7) =6
more customers - Total maximum satisfied customers =
10 + 6 = 16
The goal is to determine where to apply the technique to maximize the total number of satisfied customers.
Intuition
Let's think about what happens when we use the special technique. The technique allows us to make the owner not grumpy for minutes
consecutive minutes. This means we can "save" customers who would otherwise be unsatisfied during those grumpy minutes.
First, we can observe that there are some customers who are always satisfied - these are the customers who arrive when the owner is naturally not grumpy (when grumpy[i] = 0
). These customers will be satisfied no matter where we apply our technique.
The key insight is that we want to use our technique during the window of time where we can "save" the most customers. In other words, we need to find the consecutive minutes
where the most customers would be lost due to the owner being grumpy.
This naturally leads us to a sliding window approach:
- Calculate the baseline satisfied customers (when
grumpy[i] = 0
) - Use a sliding window of size
minutes
to find where we can maximize the additional satisfied customers - The additional customers we can satisfy in any window are those where
grumpy[i] = 1
within that window
For each possible window position, we calculate how many customers we could "save" by making the owner not grumpy during that period. The window that saves the most customers is where we should apply our technique.
The formula customers[i] * grumpy[i]
gives us the number of unsatisfied customers at minute i
(it's 0
when not grumpy, and customers[i]
when grumpy). By finding the window with the maximum sum of this value, we find the optimal time to use the technique.
Learn more about Sliding Window patterns.
Solution Approach
The solution implements a sliding window technique to find the optimal time to use the special technique.
Step 1: Initialize the sliding window
We start by calculating the number of unsatisfied customers in the first window of size minutes
:
mx = cnt = sum(c * g for c, g in zip(customers[:minutes], grumpy))
This gives us the initial window's unsatisfied customers. The expression c * g
equals customers[i]
when grumpy[i] = 1
and 0
when grumpy[i] = 0
.
Step 2: Slide the window through the array
We then slide the window one position at a time from index minutes
to the end:
for i in range(minutes, len(customers)):
cnt += customers[i] * grumpy[i]
cnt -= customers[i - minutes] * grumpy[i - minutes]
mx = max(mx, cnt)
For each new position:
- Add the new element entering the window:
customers[i] * grumpy[i]
- Remove the element leaving the window:
customers[i - minutes] * grumpy[i - minutes]
- Update
mx
to track the maximum unsatisfied customers we can save
Step 3: Calculate the final result
The total satisfied customers equals:
return sum(c * (g ^ 1) for c, g in zip(customers, grumpy)) + mx
This consists of two parts:
sum(c * (g ^ 1) for c, g in zip(customers, grumpy))
: The baseline satisfied customers (when the owner is naturally not grumpy). The XOR operationg ^ 1
flips the grumpy value, so we get1
when not grumpy and0
when grumpy.mx
: The maximum additional customers we can satisfy by using the technique optimally.
The time complexity is O(n)
where n
is the length of the arrays, and the space complexity is O(1)
as we only use a few variables to track the window state.
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 small example to illustrate the solution approach.
Given:
customers = [1, 0, 1, 2, 1, 1, 7, 5]
grumpy = [0, 1, 0, 1, 0, 1, 0, 1]
minutes = 3
Step 1: Calculate unsatisfied customers in the first window (indices 0-2)
For the first window covering minutes 0, 1, and 2:
- Minute 0:
customers[0] * grumpy[0] = 1 * 0 = 0
(not grumpy, so 0 unsatisfied) - Minute 1:
customers[1] * grumpy[1] = 0 * 1 = 0
(grumpy but no customers) - Minute 2:
customers[2] * grumpy[2] = 1 * 0 = 0
(not grumpy, so 0 unsatisfied)
Initial window sum: cnt = 0 + 0 + 0 = 0
Maximum so far: mx = 0
Step 2: Slide the window through remaining positions
Window at indices 1-3:
- Remove minute 0:
cnt -= 1 * 0 = 0
- Add minute 3:
cnt += 2 * 1 = 2
- New
cnt = 0 - 0 + 2 = 2
- Update
mx = max(0, 2) = 2
Window at indices 2-4:
- Remove minute 1:
cnt -= 0 * 1 = 0
- Add minute 4:
cnt += 1 * 0 = 0
- New
cnt = 2 - 0 + 0 = 2
mx = max(2, 2) = 2
Window at indices 3-5:
- Remove minute 2:
cnt -= 1 * 0 = 0
- Add minute 5:
cnt += 1 * 1 = 1
- New
cnt = 2 - 0 + 1 = 3
- Update
mx = max(2, 3) = 3
Window at indices 4-6:
- Remove minute 3:
cnt -= 2 * 1 = 2
- Add minute 6:
cnt += 7 * 0 = 0
- New
cnt = 3 - 2 + 0 = 1
mx = max(3, 1) = 3
Window at indices 5-7:
- Remove minute 4:
cnt -= 1 * 0 = 0
- Add minute 7:
cnt += 5 * 1 = 5
- New
cnt = 1 - 0 + 5 = 6
- Update
mx = max(3, 6) = 6
Step 3: Calculate total satisfied customers
Baseline satisfied customers (when naturally not grumpy):
- Minute 0:
1 * (0 ^ 1) = 1 * 1 = 1
- Minute 1:
0 * (1 ^ 1) = 0 * 0 = 0
- Minute 2:
1 * (0 ^ 1) = 1 * 1 = 1
- Minute 3:
2 * (1 ^ 1) = 2 * 0 = 0
- Minute 4:
1 * (0 ^ 1) = 1 * 1 = 1
- Minute 5:
1 * (1 ^ 1) = 1 * 0 = 0
- Minute 6:
7 * (0 ^ 1) = 7 * 1 = 7
- Minute 7:
5 * (1 ^ 1) = 5 * 0 = 0
Baseline sum: 1 + 0 + 1 + 0 + 1 + 0 + 7 + 0 = 10
Final result: 10 + 6 = 16
The optimal strategy is to use the technique during minutes 5-7, which saves 6 additional customers (1 from minute 5 and 5 from minute 7), giving us a total of 16 satisfied customers.
Solution Implementation
1class Solution:
2 def maxSatisfied(
3 self, customers: List[int], grumpy: List[int], minutes: int
4 ) -> int:
5 # Calculate the initial sum of unsatisfied customers in the first window of size 'minutes'
6 # This represents customers we can satisfy by using the technique in the first window
7 max_additional_satisfied = current_window_sum = sum(
8 customers[i] * grumpy[i] for i in range(minutes)
9 )
10
11 # Slide the window through the rest of the array
12 # Track the maximum number of additional customers we can satisfy
13 for i in range(minutes, len(customers)):
14 # Add the new customer entering the window (if grumpy)
15 current_window_sum += customers[i] * grumpy[i]
16 # Remove the customer leaving the window
17 current_window_sum -= customers[i - minutes] * grumpy[i - minutes]
18 # Update the maximum additional satisfied customers
19 max_additional_satisfied = max(max_additional_satisfied, current_window_sum)
20
21 # Calculate base satisfaction (customers satisfied when not grumpy)
22 # grumpy ^ 1 converts: 1 (grumpy) -> 0, and 0 (not grumpy) -> 1
23 base_satisfied = sum(
24 customers[i] * (grumpy[i] ^ 1) for i in range(len(customers))
25 )
26
27 # Total satisfaction = base satisfaction + additional customers satisfied by technique
28 return base_satisfied + max_additional_satisfied
29
1class Solution {
2 public int maxSatisfied(int[] customers, int[] grumpy, int minutes) {
3 // Calculate initial window: additional satisfied customers if we use technique in first 'minutes' window
4 int additionalSatisfiedInWindow = 0;
5 // Calculate baseline: customers already satisfied (when owner is not grumpy)
6 int baseSatisfied = 0;
7
8 // Process the first window of size 'minutes'
9 for (int i = 0; i < minutes; i++) {
10 // Add customers that would be additionally satisfied if grumpy owner uses technique
11 additionalSatisfiedInWindow += customers[i] * grumpy[i];
12 // Add customers already satisfied when owner is not grumpy (grumpy[i] XOR 1 gives opposite)
13 baseSatisfied += customers[i] * (grumpy[i] ^ 1);
14 }
15
16 // Track maximum additional satisfied customers across all possible windows
17 int maxAdditionalSatisfied = additionalSatisfiedInWindow;
18 int n = customers.length;
19
20 // Slide the window through the rest of the array
21 for (int i = minutes; i < n; i++) {
22 // Add new element entering the window
23 additionalSatisfiedInWindow += customers[i] * grumpy[i];
24 // Remove element leaving the window
25 additionalSatisfiedInWindow -= customers[i - minutes] * grumpy[i - minutes];
26 // Update maximum additional satisfied customers
27 maxAdditionalSatisfied = Math.max(maxAdditionalSatisfied, additionalSatisfiedInWindow);
28 // Add baseline satisfied customers for positions outside initial window
29 baseSatisfied += customers[i] * (grumpy[i] ^ 1);
30 }
31
32 // Return total: baseline satisfied + maximum additional satisfied by using technique
33 return baseSatisfied + maxAdditionalSatisfied;
34 }
35}
36
1class Solution {
2public:
3 int maxSatisfied(vector<int>& customers, vector<int>& grumpy, int minutes) {
4 // Calculate initial window sum and base satisfaction
5 int windowSum = 0; // Sum of unsatisfied customers in current window
6 int baseSatisfaction = 0; // Sum of already satisfied customers
7
8 // Initialize the first window of size 'minutes'
9 for (int i = 0; i < minutes; ++i) {
10 // Add unsatisfied customers to window sum
11 windowSum += customers[i] * grumpy[i];
12 // Add already satisfied customers to base satisfaction
13 baseSatisfaction += customers[i] * (grumpy[i] ^ 1);
14 }
15
16 int maxWindowSum = windowSum; // Track maximum unsatisfied customers we can satisfy
17 int n = customers.size();
18
19 // Slide the window through the rest of the array
20 for (int i = minutes; i < n; ++i) {
21 // Add new element to window
22 windowSum += customers[i] * grumpy[i];
23 // Remove element that's sliding out of window
24 windowSum -= customers[i - minutes] * grumpy[i - minutes];
25 // Update maximum window sum
26 maxWindowSum = max(maxWindowSum, windowSum);
27 // Add already satisfied customers outside the window to base satisfaction
28 baseSatisfaction += customers[i] * (grumpy[i] ^ 1);
29 }
30
31 // Return total: base satisfaction + maximum additional satisfaction from using technique
32 return baseSatisfaction + maxWindowSum;
33 }
34};
35
1function maxSatisfied(customers: number[], grumpy: number[], minutes: number): number {
2 // Initialize variables to track additional satisfied customers and base satisfied customers
3 let additionalSatisfied: number = 0;
4 let baseSatisfied: number = 0;
5
6 // Calculate initial window of size 'minutes'
7 // additionalSatisfied: customers that can be made satisfied by using the technique
8 // baseSatisfied: customers already satisfied when owner is not grumpy
9 for (let i = 0; i < minutes; i++) {
10 additionalSatisfied += customers[i] * grumpy[i];
11 baseSatisfied += customers[i] * (grumpy[i] ^ 1);
12 }
13
14 // Track the maximum additional customers that can be satisfied
15 let maxAdditional: number = additionalSatisfied;
16
17 // Slide the window through the rest of the array
18 for (let i = minutes; i < customers.length; i++) {
19 // Add new element to the window (right side)
20 additionalSatisfied += customers[i] * grumpy[i];
21
22 // Remove element that's leaving the window (left side)
23 additionalSatisfied -= customers[i - minutes] * grumpy[i - minutes];
24
25 // Update maximum additional satisfied customers
26 maxAdditional = Math.max(maxAdditional, additionalSatisfied);
27
28 // Add naturally satisfied customers (when owner is not grumpy)
29 baseSatisfied += customers[i] * (grumpy[i] ^ 1);
30 }
31
32 // Return total: base satisfied customers + maximum additional satisfied customers
33 return baseSatisfied + maxAdditional;
34}
35
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array customers
. The algorithm performs the following operations:
- Initial calculation of the first window takes
O(minutes)
time - The sliding window loop runs from index
minutes
ton-1
, performingO(1)
operations in each iteration, resulting inO(n - minutes)
time - The final sum calculation iterates through all elements once, taking
O(n)
time - Overall:
O(minutes) + O(n - minutes) + O(n) = O(n)
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space for variables mx
and cnt
, regardless of the input size. The zip operations and iterations don't create additional data structures that scale with input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Window Calculation
A common mistake is incorrectly calculating what the sliding window represents. The window should track unsatisfied customers that can be made satisfied, not all customers in the window.
Incorrect approach:
# Wrong: This counts ALL customers in the window
window_sum = sum(customers[i] for i in range(minutes))
Correct approach:
# Right: Only count customers when the owner IS grumpy
window_sum = sum(customers[i] * grumpy[i] for i in range(minutes))
2. Off-by-One Error in Window Sliding
Another pitfall is incorrectly managing the window boundaries when sliding, especially when removing the element that's leaving the window.
Incorrect approach:
# Wrong: Using wrong index for the element leaving the window
for i in range(minutes, len(customers)):
cnt += customers[i] * grumpy[i]
cnt -= customers[i - minutes + 1] * grumpy[i - minutes + 1] # Wrong offset!
Correct approach:
# Right: Proper index calculation
for i in range(minutes, len(customers)):
cnt += customers[i] * grumpy[i]
cnt -= customers[i - minutes] * grumpy[i - minutes] # Correct offset
3. Forgetting Edge Cases
Failing to handle edge cases where minutes
equals or exceeds the array length.
Solution: Add validation:
if minutes >= len(customers):
# If we can make the owner not grumpy for the entire day
return sum(customers)
4. Incorrect Base Satisfaction Calculation
Computing the baseline satisfied customers incorrectly by not properly inverting the grumpy values.
Incorrect approach:
# Wrong: This counts customers when grumpy, not when satisfied
base = sum(c * g for c, g in zip(customers, grumpy))
Correct approach:
# Right: Count customers when NOT grumpy (grumpy == 0)
base = sum(c * (g ^ 1) for c, g in zip(customers, grumpy))
# Or alternatively:
base = sum(c for c, g in zip(customers, grumpy) if g == 0)
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 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
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 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
Want a Structured Path to Master System Design Too? Don’t Miss This!