1700. Number of Students Unable to Eat Lunch
Problem Description
In this problem, we have a queue of students and a stack of sandwiches. The sandwiches can be of two types, represented by 0
(circular) and 1
(square). Each student in the queue has a preference for either circular or square sandwiches.
Students are served in the order they are standing in the queue. If the student at the front of the queue prefers the type of sandwich that is on top of the sandwich stack, they take the sandwich and leave the queue. However, if they do not prefer the type of sandwich on top of the stack, they pass on the sandwich and move to the end of the queue.
The process stops when the sandwich on the top of the stack is not preferred by any of the students left in the queue, meaning no more sandwiches can be served, and some students will be unable to eat. The task is to find the number of students who will not be able to eat a sandwich.
Intuition
The key insight into solving this problem lies in recognizing that if a certain type of sandwich could not be taken by any student in the queue, no further progress can be made. This is because the students' order and preferences do not change.
We start with two counts: how many students prefer circular sandwiches and how many prefer square ones. We process the sandwich stack from the top (i = 0
being the top of the stack). For each sandwich, we check if there is a student in the queue who prefers that type of sandwich. If there is, we decrease the count of students waiting for that type of sandwich. If there isn't, it means all remaining students prefer the other type of sandwich and will be unable to eat. At this point, we can return the number of students who prefer the other type of sandwich.
This approach ensures we check the selection possibility for each sandwich without actually moving students around in the queue, which results in a time-efficient solution.
The solution implementation is straightforward: we keep counts of students' preferences and iterate through the sandwich stack. When we encounter a sandwich with no matching student preference, we stop and return the count of the other type of sandwich. If we go through the whole stack, it means all students got their preferred sandwich, and we return 0
.
Solution Approach
The solution provided uses the Counter
class from Python's collections
module to efficiently keep track of the count of students' sandwich preferences.
Here's a step-by-step explanation of the implementation:
-
A
Counter
object calledcnt
is created from thestudents
list. This will give us a dictionary-like object where the keys are0
and1
, and the values are the count of students preferring circular (0
) and square (1
) sandwiches respectively. -
The solution then iterates over the
sandwiches
stack with a for-loop. For each sandwich typev
at the top of the stack:a. The solution checks if
cnt[v]
equals0
. If it is, this means there are no students left that prefer the current type of sandwich on top of the stack. Since the students who prefer the other type of sandwich won't take the current one, the solution returnscnt[v ^ 1]
. The expressionv ^ 1
is a bitwise XOR operation that toggles between0
and1
, effectively giving us the count of the other sandwich preference.b. If
cnt[v]
does not equal0
, which means there is at least one student who prefers the type of sandwich on top of the stack, the solution decrements the count of that sandwich preference by1
since that student takes the sandwich and leaves the queue. -
After the for-loop, if all sandwiches have been successfully served to students, the final return value is
0
, meaning no students are left unable to eat.
The for-loop
in this approach works effectively because it processes the sandwiches in the natural order they are being served, and the Counter
keeps track of the dynamic preference count without the need to manage the queue of students manually.
By running this efficient loop and conditions, the algorithm ensures a linear time complexity proportional to the number of sandwiches (or equivalently the number of students), which is O(n)
where n
is the number of students or the length of the sandwiches
array.
Therefore, the algorithm and the usage of the Counter
data structure work together to provide a solution that is straightforward, readable, and efficient in both time and space.
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 a small example to illustrate the solution approach:
Suppose we have the following queue of students and stack of sandwiches:
Students preferences (queue): [1, 0, 0, 1, 1] Sandwiches stack (top to bottom): [0, 1, 0, 1, 1]
Each 1
in the students' queue represents a student who prefers square sandwiches and each 0
represents a student who prefers circular sandwiches.
Step 1:
- We initialize a
Counter
object from the students' preferences:cnt = Counter([1, 0, 0, 1, 1])
, resulting incnt = {0: 2, 1: 3}
. This means 2 students prefer circular sandwiches and 3 prefer square ones.
Step 2:
- Now, we start iterating over the
sandwiches
stack from the top:
Iteration 1:
- The top of the stack is a circular sandwich (
0
).cnt[0]
is not0
, so there's a student who prefers this sandwich. - We decrement
cnt[0]
by1
, leavingcnt
as{0: 1, 1: 3}
.
Iteration 2:
- Next sandwich is square (
1
).cnt[1]
is not0
, indicating a matching student preference. - We decrement
cnt[1]
by1
, makingcnt
{0: 1, 1: 2}
.
Iteration 3:
- Another circular sandwich (
0
) is on top.cnt[0]
is not0
, so a student will take this sandwich. - We decrement
cnt[0]
by1
and nowcnt
is{0: 0, 1: 2}
.
Iteration 4:
- The next sandwich is square (
1
).cnt[1]
is not0
, so a student prefers and takes it. - We decrement
cnt[1]
to1
, updatingcnt
to{0: 0, 1: 1}
.
Iteration 5:
- The last sandwich is also square (
1
).cnt[1]
is not0
. - We decrement
cnt[1]
to0
, finalcnt
is{0: 0, 1: 0}
.
Step 3:
- We have successfully iterated over all the sandwiches, and the
cnt
for both types of sandwiches is0
, meaning each student got their preference.
Final step:
- The final return value is
0
since no students are left unable to eat.
This small example demonstrates how we can efficiently determine that all students will be able to eat by just keeping track of the counts of their sandwich preferences and decrementing the respective counts as we match students with sandwiches. No actual rearrangement of the queue is required. If at any point we discovered a sandwich on top that matches none of the students' remaining preferences, we would return the number of students left with the non-matching preference immediately.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def countStudents(self, students: List[int], sandwiches: List[int]) -> int:
5 # Count the number of students preferring each type of sandwich
6 count = Counter(students)
7
8 # Loop through the sandwiches queue
9 for sandwich in sandwiches:
10 # If no student wants the current type of sandwich,
11 # return the number of students who want the other type
12 if count[sandwich] == 0:
13 # The 'sandwich ^ 1' toggles between 1 and 0
14 return count[sandwich ^ 1]
15
16 # Decrement the count for the current sandwich type
17 count[sandwich] -= 1
18
19 # If all sandwiches match the students' preferences return 0
20 return 0
21
1class Solution {
2 public int countStudents(int[] students, int[] sandwiches) {
3 // Array to count the number of students who prefer each type of sandwich
4 // cnt[0] for students who prefer sandwich type 0, and cnt[1] for sandwich type 1
5 int[] count = new int[2];
6
7 // Count the number of students preferring each type of sandwich
8 for (int student : students) {
9 count[student]++;
10 }
11
12 // Iterate through the sandwiches
13 for (int sandwich : sandwiches) {
14 // If no students want the current type of sandwich, break and return the number of students
15 // who want the other type of sandwich
16 if (count[sandwich] == 0) {
17 // The other type is obtained by XORing the current type with 1
18 // If current is 0, 0^1 will be 1 (the other type)
19 // If current is 1, 1^1 will be 0 (the other type)
20 return count[sandwich ^ 1];
21 }
22 // Decrement the count as one student takes a sandwich of the type they prefer
23 count[sandwich]--;
24 }
25
26 // If all students got their preferred sandwiches, return 0
27 return 0;
28 }
29}
30
1#include <vector> // make sure to include the vector header for vector support
2
3class Solution {
4public:
5 // Function to count the number of students who can't get a sandwich
6 int countStudents(vector<int>& students, vector<int>& sandwiches) {
7 // Initialize a count array to store the number of students preferring each type of sandwich
8 int count[2] = {0};
9
10 // Count the number of students preferring each type of sandwich (0 or 1)
11 for (int student : students) {
12 count[student]++;
13 }
14
15 // Iterate through the sandwiches queue
16 for (int sandwich : sandwiches) {
17 // If no students prefer the current type of sandwich, break and return the count of the other type
18 if (count[sandwich] == 0) {
19 return count[1 - sandwich]; // use 1 - sandwich to get the opposite type
20 }
21 // Otherwise, decrement the count of students who prefer the current type of sandwich
22 count[sandwich]--;
23 }
24
25 // If all sandwiches can be taken by students, return 0
26 return 0;
27 }
28};
29
1// Function to count the number of students who will not be able to get their preferred sandwich
2function countStudents(students: number[], sandwiches: number[]): number {
3 // Initialize a count array where index 0 represents students who prefer sandwich type 0,
4 // and index 1 represents students who prefer sandwich type 1.
5 const sandwichPreferenceCount = [0, 0];
6
7 // Count the number of students who prefer each type of sandwich
8 for (const studentPreference of students) {
9 sandwichPreferenceCount[studentPreference]++;
10 }
11
12 // Iterate over the sandwich types in the order they are to be served
13 for (const sandwichType of sandwiches) {
14 // If there are no students left preferring the current sandwich type,
15 // return the count of students preferring the other type
16 if (sandwichPreferenceCount[sandwichType] === 0) {
17 return sandwichPreferenceCount[sandwichType ^ 1];
18 }
19 // Otherwise, decrement the count of students who prefer the current sandwich type
20 sandwichPreferenceCount[sandwichType]--;
21 }
22
23 // If all students can be served with their preferred sandwich type,
24 // return 0 indicating no students are left unserved
25 return 0;
26}
27
Time and Space Complexity
The given Python code snippet implements a function to count the number of students who won't be able to eat their preferred type of sandwich. The time and space complexities of the code are as follows:
- Time complexity:
The function iterates over each sandwich in the sandwiches
list, which has a time complexity of O(n)
, where n
is the number of sandwiches (or students, since they are the same). The operation cnt[v]
and the decrement operation cnt[v] -= 1
both operate in constant time, O(1)
. Therefore, the overall time complexity of the function is O(n)
.
- Space complexity:
The space complexity is primarily defined by the space required to store the counter for the students. The Counter
object stores an entry for each unique value in the students
list, which, in this case, has only two possible values (0 and 1). Therefore, the Counter
object space is constant, O(1)
. Hence, the space complexity of the function is O(1)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Which of the following array represent a max heap?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Queue Intro Think of the last time you stood in line to buy movie tickets The first person in line gets their ticket first and any newcomers join the end of the line This system where the first person to arrive is the first to be served is a queue in real
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
Want a Structured Path to Master System Design Too? Don’t Miss This!