Facebook Pixel

2753. Count Houses in a Circular Street II 🔒

Problem Description

You have a circular street with houses arranged in a circle. Each house has a door that can be either open or closed. You're given:

  • An object street that represents this circular street
  • A positive integer k that represents the maximum possible number of houses (the actual number of houses ≤ k)
  • At least one door is initially open

You start standing in front of one house on this street. Your goal is to count the total number of houses on the street.

The Street class provides three methods to interact with the street:

  • closeDoor(): Closes the door of the house you're currently facing
  • isDoorOpen(): Returns true if the current house's door is open, false if closed
  • moveRight(): Moves you to the next house to the right

Since the street is circular, if houses are numbered from 1 to n, then:

  • The right neighbor of house i is house i+1 (for i < n)
  • The right neighbor of house n is house 1 (wraps around)

The solution works by:

  1. First finding an open door by moving right until one is found
  2. Then moving right one house at a time (up to k times)
  3. Each time an open door is encountered, recording the current position i as the potential answer and closing that door
  4. When you complete the circle and return to the originally open door (which is now the only open door), the last recorded position gives the total number of houses

The key insight is that by closing doors as you go and tracking when you encounter an open door again, you can determine when you've made a complete circle around the street.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The challenge here is that we don't know our starting position or how many houses there are - we just know we're standing somewhere on a circular street. How can we count houses when we don't know where we are or when we've completed a full circle?

The key realization is that we need a way to mark our starting point so we know when we've returned to it. Since we can close doors, we can use door states as markers.

Think of it like leaving breadcrumbs: if we start from an open door and close it, then keep moving right and closing any open doors we find, eventually we'll complete the circle and return to find an open door again. But here's the clever part - the only open door we'll find must be the one we started from (since we closed all others along the way).

So the strategy becomes:

  1. First, ensure we're starting at an open door (keep moving until we find one)
  2. As we move right, count our steps
  3. Whenever we see an open door, we know this could potentially be our starting point returning into view
  4. Close that door to mark that we've been here
  5. Keep going until we've checked up to k houses (the maximum possible)

The last time we encounter an open door before our loop ends tells us exactly how many houses we traveled to complete the circle. For example, if we find an open door after moving right 5 times, we know there are exactly 5 houses on the street.

This works because in a circular street with n houses, moving right n times brings you back to your starting position. By closing doors as we go and tracking when we see the next open door, we're effectively measuring the circumference of our circular street.

Solution Approach

The implementation follows a two-phase approach:

Phase 1: Find a Starting Open Door

while not street.isDoorOpen():
    street.moveRight()

We first ensure we're standing at an open door. This gives us a consistent starting point for our counting process. We keep moving right until we find an open door.

Phase 2: Count Houses by Making a Complete Circle

for i in range(1, k + 1):
    street.moveRight()
    if street.isDoorOpen():
        ans = i
        street.closeDoor()

Starting from our open door position, we:

  1. Move right one house at a time, iterating from 1 to k (the maximum possible number of houses)
  2. After each move, check if the current door is open using isDoorOpen()
  3. If we find an open door:
    • Record the current step count i in variable ans - this represents how many houses we've moved
    • Close this door using closeDoor() to mark that we've visited it

The algorithm cleverly exploits the circular nature of the street. When we encounter an open door after starting from an open door (and closing all doors along the way), it must be our starting position. The number of steps taken to reach it equals the total number of houses.

Why This Works:

  • Initially, at least one door is open (guaranteed by the problem)
  • As we traverse and close doors, we create a trail of closed doors
  • In a circular street with n houses, moving right n times returns us to the starting position
  • The last open door we encounter is our starting point coming back into view
  • The variable ans gets updated each time we see an open door, so the final value represents the complete circle

Time Complexity: O(k) where k is the maximum number of houses Space Complexity: O(1) as we only use a constant amount of extra space

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example where we have a circular street with 5 houses, and we're initially standing at house 3. Houses 2 and 4 have open doors, while houses 1, 3, and 5 have closed doors.

Initial Setup:

Houses: [1] [2] [3] [4] [5] → back to [1] (circular)
Doors:   C   O   C   O   C
Position:        ^

(C = Closed, O = Open, ^ = our position)

Phase 1: Find an Open Door

Since we're at house 3 with a closed door, we need to find an open door first:

  • Check current door (house 3): Closed
  • Move right to house 4
  • Check door: Open! Stop here.
Houses: [1] [2] [3] [4] [5]
Doors:   C   O   C   O   C
Position:            ^

Phase 2: Count Houses by Circling

Now we start our counting loop (i = 1 to k):

Step i=1: Move right to house 5

Houses: [1] [2] [3] [4] [5]
Doors:   C   O   C   O   C
Position:                ^
  • Check door: Closed
  • Continue

Step i=2: Move right to house 1 (wrapping around)

Houses: [1] [2] [3] [4] [5]
Doors:   C   O   C   O   C
Position: ^
  • Check door: Closed
  • Continue

Step i=3: Move right to house 2

Houses: [1] [2] [3] [4] [5]
Doors:   C   O   C   O   C
Position:     ^
  • Check door: Open!
  • Set ans = 3
  • Close this door
Houses: [1] [2] [3] [4] [5]
Doors:   C   C   C   O   C
Position:     ^

Step i=4: Move right to house 3

Houses: [1] [2] [3] [4] [5]
Doors:   C   C   C   O   C
Position:         ^
  • Check door: Closed
  • Continue

Step i=5: Move right to house 4

Houses: [1] [2] [3] [4] [5]
Doors:   C   C   C   O   C
Position:             ^
  • Check door: Open!
  • Set ans = 5
  • Close this door
Houses: [1] [2] [3] [4] [5]
Doors:   C   C   C   C   C
Position:             ^

At this point, we've completed exactly one full circle (5 moves from house 4 back to house 4). The last time we encountered an open door was at step 5, which correctly indicates there are 5 houses on the street.

The algorithm would continue until i = k, but no more open doors would be found since we've closed them all. The final answer remains 5, which is the correct count of houses on this circular street.

Solution Implementation

1# Definition for a street.
2# class Street:
3#     def closeDoor(self):
4#         pass
5#     def isDoorOpen(self):
6#         pass
7#     def moveRight(self):
8#         pass
9
10from typing import Optional
11
12class Solution:
13    def houseCount(self, street: Optional["Street"], k: int) -> int:
14        """
15        Count the number of houses on a circular street.
16      
17        Args:
18            street: Street object with methods to check/close doors and move
19            k: Maximum number of steps to take
20          
21        Returns:
22            Number of houses on the street
23        """
24        # Move to the first open door (starting position)
25        while not street.isDoorOpen():
26            street.moveRight()
27      
28        # Initialize answer variable to track position of closed door
29        ans = 0
30      
31        # Move k steps, closing the first open door we encounter
32        # This helps us identify when we've made a complete circle
33        for i in range(1, k + 1):
34            street.moveRight()
35            if street.isDoorOpen():
36                # Found an open door - record its position and close it
37                ans = i
38                street.closeDoor()
39      
40        return ans
41
1/**
2 * Definition for a street.
3 * class Street {
4 *     public Street(int[] doors);
5 *     public void closeDoor();
6 *     public boolean isDoorOpen();
7 *     public void moveRight();
8 * }
9 */
10class Solution {
11    /**
12     * Counts the number of houses on a circular street.
13     * 
14     * @param street The street object representing a circular street with houses
15     * @param k The maximum number of houses possible on the street
16     * @return The actual number of houses on the street
17     */
18    public int houseCount(Street street, int k) {
19        // Move to the first open door to establish a starting position
20        while (!street.isDoorOpen()) {
21            street.moveRight();
22        }
23      
24        // Initialize the house count
25        int houseCount = 0;
26      
27        // Traverse the street for at most k positions
28        // We check each position and mark the last open door we find
29        for (int position = 1; position <= k; position++) {
30            street.moveRight();
31          
32            // If we find an open door, update our count and close it as a marker
33            if (street.isDoorOpen()) {
34                houseCount = position;
35                street.closeDoor();
36            }
37        }
38      
39        // The last position where we found an open door represents the total house count
40        return houseCount;
41    }
42}
43
1/**
2 * Definition for a street.
3 * class Street {
4 * public:
5 *     Street(vector<int> doors);
6 *     void closeDoor();
7 *     bool isDoorOpen();
8 *     void moveRight();
9 * };
10 */
11class Solution {
12public:
13    int houseCount(Street* street, int k) {
14        // Move right until we find an open door (starting position)
15        while (!street->isDoorOpen()) {
16            street->moveRight();
17        }
18      
19        // Initialize the answer to track the last position with an open door
20        int answer = 0;
21      
22        // Explore k positions to the right from the starting position
23        for (int i = 1; i <= k; ++i) {
24            street->moveRight();
25          
26            // If we find an open door at position i
27            if (street->isDoorOpen()) {
28                // Update the answer with the current position
29                answer = i;
30                // Close the door to mark it as visited
31                street->closeDoor();
32            }
33        }
34      
35        // Return the position of the last open door found
36        return answer;
37    }
38};
39
1/**
2 * Counts the number of houses on a circular street.
3 * The street is circular, and we use door states to track our position.
4 * 
5 * @param street - The street object with doors that can be opened/closed
6 * @param k - The maximum number of steps to explore
7 * @returns The total number of houses on the street
8 */
9function houseCount(street: Street | null, k: number): number {
10    // Move to the first open door to establish a starting position
11    while (!street.isDoorOpen()) {
12        street.moveRight();
13    }
14  
15    // Track the furthest position where we find an open door
16    let houseCount: number = 0;
17  
18    // Explore up to k positions to the right
19    for (let step: number = 1; step <= k; ++step) {
20        street.moveRight();
21      
22        // If we find an open door, update our count and close it as a marker
23        if (street.isDoorOpen()) {
24            houseCount = step;
25            street.closeDoor();
26        }
27    }
28  
29    return houseCount;
30}
31

Time and Space Complexity

Time Complexity: O(k)

The algorithm consists of two main parts:

  1. The first while loop that moves right until finding an open door - this takes at most k moves since we're on a circular street with k houses
  2. The for loop that iterates exactly k times, performing constant time operations (moveRight(), isDoorOpen(), and potentially closeDoor()) in each iteration

Since both parts are bounded by k, the overall time complexity is O(k).

Space Complexity: O(1)

The algorithm only uses a constant amount of extra space:

  • A loop counter i in the for loop
  • A variable ans to store the result

No additional data structures are used that grow with the input size, so the space complexity is constant.

Common Pitfalls

Pitfall 1: Misunderstanding the Loop Logic

Issue: A common mistake is thinking we need to count ALL open doors or that we should stop immediately after finding the first open door in the counting phase. This misunderstanding leads to incorrect implementations like:

# INCORRECT: Stops too early
for i in range(1, k + 1):
    street.moveRight()
    if street.isDoorOpen():
        return i  # Wrong! This returns too early

Why it's wrong: The algorithm needs to close doors as it encounters them to mark the path. When we return to our starting position (which is the only remaining open door after we've closed all others), that final position tells us the total count.

Correct approach: Continue the loop and keep updating ans whenever an open door is found:

for i in range(1, k + 1):
    street.moveRight()
    if street.isDoorOpen():
        ans = i  # Update answer
        street.closeDoor()  # Mark as visited

Pitfall 2: Not Handling the Initial Position Correctly

Issue: Forgetting to first position yourself at an open door before starting the counting process:

# INCORRECT: Starts counting without ensuring we're at an open door
ans = 0
for i in range(1, k + 1):
    street.moveRight()
    if street.isDoorOpen():
        ans = i
        street.closeDoor()
return ans

Why it's wrong: If you start at a closed door, you might miss the actual pattern of the circular street or get an off-by-one error in your count.

Correct approach: Always ensure you start from an open door:

# First, find an open door
while not street.isDoorOpen():
    street.moveRight()
  
# Then start counting
for i in range(1, k + 1):
    # ... rest of the logic

Pitfall 3: Overcounting or Using Wrong Range

Issue: Using range(k) instead of range(1, k + 1) or trying to count from 0:

# INCORRECT: Wrong range
for i in range(k):  # This goes from 0 to k-1
    street.moveRight()
    if street.isDoorOpen():
        ans = i  # Will be 0 for the first open door found
        street.closeDoor()

Why it's wrong: Starting from 0 means the first open door encountered after moving once would be recorded as position 0, which doesn't represent the actual count of houses moved. We need to count from 1 since each iteration represents moving to the next house.

Correct approach: Use range(1, k + 1) to properly count houses:

for i in range(1, k + 1):  # Counts from 1 to k
    street.moveRight()
    if street.isDoorOpen():
        ans = i  # Correctly records the number of moves made
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How many times is a tree node visited in a depth first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More