Number of Ways to Divide a Long Corridor
HardPrompt
Along a long library corridor, there is a line of seats and decorative plants. You are given a 0-indexed string corridor of length n consisting of letters 'S' and 'P' where each 'S' represents a seat and each 'P' represents a plant.
One room divider has already been installed to the left of index 0, and another to the right of index n - 1. Additional room dividers can be installed. For each position between indices i - 1 and i (1 <= i <= n - 1), at most one divider can be installed.
Divide the corridor into non-overlapping sections, where each section has exactly two seats with any number of plants. There may be multiple ways to perform the division. Two ways are different if there is a position with a room divider installed in the first way but not in the second way.
Return the number of ways to divide the corridor. Since the answer may be very large, return it modulo 109 + 7. If there is no way, return 0.
Example 1:
Input: corridor = "SSPPSPS"
Output: 3
Explanation: There are 3 different ways to divide the corridor.
The black bars in the above image indicate the two room dividers already installed.
Note that in each of the ways, each section has exactly two seats.Example 2:
Input: corridor = "PPSPSP"
Output: 1
Explanation: There is only 1 way to divide the corridor, by not installing any additional dividers.
Installing any would create some section that does not have exactly two seats.Example 3:
Input: corridor = "S"
Output: 0
Explanation: There is no way to divide the corridor because there will always be a section that does not have exactly two seats.
Constraints:
n == corridor.length1 <= n <= 105corridor[i]is either'S'or'P'.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves two main steps. First, we iterate through the entire corridor to identify and store the indices of all seats ('S'). After collecting all seat indices, we perform a second pass on this list of indices to calculate the number of ways to place dividers.
Algorithm
- Create a list,
seatIndices, to store the 0-based index of each seat 'S'. - Iterate through the input
corridorstring. Ifcorridor[i]is 'S', additoseatIndices. - Check the size of
seatIndices. If it's 0 or an odd number, return 0, as no valid division is possible. - Initialize a
longvariablewaysto 1 andMOD = 1_000_000_007. - Iterate through
seatIndicesfrom the 3rd seat (i = 2) to the end, with a step of 2. In each step, we are looking at the gap between a pair of seats, e.g., between the 2nd and 3rd, 4th and 5th, and so on. - For each
i, calculate the number of divider placements available between the previous pair and the current pair. This isseatIndices.get(i) - seatIndices.get(i-1). - Multiply
waysby this difference and take the moduloMOD:ways = (ways * (seatIndices.get(i) - seatIndices.get(i-1))) % MOD; - Return
(int) ways.
Walkthrough
The core idea is that dividers can only be placed between pairs of seats. For a valid division, the corridor must be partitioned into sections each containing exactly two seats. This implies that the total number of seats must be an even number and greater than zero.
We first scan the corridor string and store the indices of every 'S' in a list. If the total count of seats is zero or odd, no valid division is possible, so we return 0.
If the count is valid, we know that the first section must contain the first two seats, the second section must contain the third and fourth seats, and so on. A divider must be placed between the second seat of one section and the first seat of the next. For example, between the 2nd and 3rd seats, between the 4th and 5th seats, etc.
The number of ways to place a divider between the i-th pair and the (i+1)-th pair is the number of available slots between the second seat of the i-th pair and the first seat of the (i+1)-th pair. This is equal to the difference in their indices.
We calculate the total number of ways by multiplying the number of choices for each required divider, as these choices are independent. The final result is taken modulo 10^9 + 7.
class Solution { public int numberOfWays(String corridor) { long MOD = 1_000_000_007; java.util.List<Integer> seatIndices = new java.util.ArrayList<>(); for (int i = 0; i < corridor.length(); i++) { if (corridor.charAt(i) == 'S') { seatIndices.add(i); } } int numSeats = seatIndices.size(); if (numSeats == 0 || numSeats % 2 != 0) { return 0; } long ways = 1; // We are interested in the gaps between pairs of seats. // The first gap is between the 2nd and 3rd seats. // The second gap is between the 4th and 5th seats, and so on. for (int i = 2; i < numSeats; i += 2) { int prevSeatIndex = seatIndices.get(i - 1); int currSeatIndex = seatIndices.get(i); long diff = currSeatIndex - prevSeatIndex; ways = (ways * diff) % MOD; } return (int) ways; }}Complexity
Time
O(N), where N is the length of the corridor. The first pass to find seats takes O(N), and the second pass over the seat indices takes O(S) where S is the number of seats (S <= N).
Space
O(S), which can be up to O(N) in the worst case where most or all characters are seats. This is for storing the `seatIndices` list.
Trade-offs
Pros
The logic is straightforward and easy to understand.
It correctly solves the problem by identifying the core constraint on divider placement.
Cons
Requires extra space to store the indices of all seats, which can be up to O(N) in the worst case.
It requires two passes over the data (one over the string, one over the list of indices).
This approach improves upon the previous one by processing the corridor in a single pass and using only a few variables to keep track of the state, thus achieving constant space complexity.
Algorithm
- Initialize
seatCount = 0,ways = 1L, andprevSeatIndex = -1. DefineMOD = 1_000_000_007. - Iterate through the
corridorstring from left to right with indexi. - If
corridor.charAt(i) == 'S':- Increment
seatCount. - If
seatCountis odd and greater than or equal to 3, it marks the beginning of a new pair. The gap for a divider is between this seat and the previous one. - Calculate the difference:
diff = i - prevSeatIndex. - Update
ways:ways = (ways * diff) % MOD. - Update
prevSeatIndexto the current indexi.
- Increment
- After the loop, if
seatCountis 0 or odd, return 0. - Otherwise, return
(int) ways.
Walkthrough
Instead of storing all seat indices, we can calculate the number of ways on the fly. We iterate through the corridor, keeping a count of the seats encountered so far.
The key insight remains the same: the number of ways is the product of the number of possible divider positions between consecutive pairs of seats. A choice for a divider position arises only after we have found a complete pair of seats and are about to start a new pair. This happens when we encounter the 3rd, 5th, 7th, etc., seat.
We use a variable seatCount to track the number of seats seen. We also need prevSeatIndex to remember the index of the previously seen seat.
When we find a seat S:
- We increment
seatCount. - If
seatCountbecomes odd and is greater than 2 (e.g., 3, 5, ...), it means we've just started a new pair. The previous seat (atprevSeatIndex) was the end of the last pair. The number of ways to place a divider between them iscurrentIndex - prevSeatIndex. We multiply our totalwaysby this value. - We then update
prevSeatIndexto the current seat's index.
After the loop, we must check if the total seatCount is valid (even and non-zero). If not, the answer is 0.
class Solution { public int numberOfWays(String corridor) { long MOD = 1_000_000_007; long ways = 1; int seatCount = 0; int prevSeatIndex = -1; for (int i = 0; i < corridor.length(); i++) { if (corridor.charAt(i) == 'S') { seatCount++; if (seatCount >= 2 && seatCount % 2 == 1) { // This is the 3rd, 5th, 7th, etc., seat. // It marks the start of a new section. // The gap is between this seat and the previous one. long diff = i - prevSeatIndex; ways = (ways * diff) % MOD; } prevSeatIndex = i; } } if (seatCount == 0 || seatCount % 2 != 0) { return 0; } return (int) ways; }}Complexity
Time
O(N), as we iterate through the string once.
Space
O(1), as we only use a few variables to store the state, regardless of the input size.
Trade-offs
Pros
Highly efficient in both time and space.
Processes the input in a single pass.
Uses constant extra space, making it suitable for very large inputs.
Cons
The logic might be slightly less intuitive at first glance compared to the two-pass approach.
Solutions
Solution
class Solution {private String s;private int n;private int[][] f;private static final int MOD = (int)1 e9 + 7;public int numberOfWays(String corridor) { s = corridor; n = s.length(); f = new int[n][3]; for (var e : f) { Arrays.fill(e, -1); } return dfs(0, 0); }private int dfs(int i, int cnt) { if (i == n) { return cnt == 2 ? 1 : 0; } cnt += s.charAt(i) == 'S' ? 1 : 0; if (cnt > 2) { return 0; } if (f[i][cnt] != -1) { return f[i][cnt]; } int ans = dfs(i + 1, cnt); if (cnt == 2) { ans += dfs(i + 1, 0); ans %= MOD; } f[i][cnt] = ans; return ans; }}class Solution {public: const int mod = 1e9 + 7; int numberOfWays(string corridor) { int n = corridor.size(); vector<vector<int>> f(n, vector<int>(3, -1)); function<int(int, int)> dfs; dfs = [&](int i, int cnt) { if (i == n) return cnt == 2 ? 1 : 0; cnt += corridor[i] == 'S'; if (cnt > 2) return 0; if (f[i][cnt] != -1) return f[i][cnt]; int ans = dfs(i + 1, cnt); if (cnt == 2) { ans += dfs(i + 1, 0); ans %= mod; } f[i][cnt] = ans; return ans; }; return dfs(0, 0); }};class Solution: def numberOfWays(self, corridor: str) -> int: @ cache def dfs(i, cnt): if i == n: return int(cnt == 2) cnt += corridor[i] == 'S' if cnt > 2: return 0 ans = dfs(i + 1, cnt) if cnt == 2: ans += dfs(i + 1, 0) ans %= mod return ans n = len(corridor) mod = 10 ** 9 + 7 ans = dfs(0, 0) dfs . cache_clear() return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.