You are given an array cells
of length 8, where each element is either 0
(cell is vacant) or 1
(cell is occupied). Each day, the state of the cells changes according to the following rules:
Given an integer N
, representing the number of days, return the state of the cells after N
days. The initial state is provided by the input array cells
.
Constraints:
cells.length == 8
cells[i]
is 0
or 1
1 <= N <= 10^9
At first glance, it seems we need to simulate each day, updating the array according to the rules, for up to N
days. However, since N
can be as large as a billion, simulating each day one by one would be too slow.
Notice that the number of possible states is limited. With 8 cells and each cell being 0 or 1, there are at most 256 possible states. However, the first and last cells always become 0, so only the 6 middle cells can vary, leading to 64 possible states. This suggests that the process must eventually repeat (cycle).
Therefore, rather than simulating all N
days, we can simulate until we see a repeated state and then use the cycle length to skip ahead, reducing the number of simulations needed.
N
to a much smaller number of steps.
Input: cells = [0,1,0,1,1,0,0,1]
, N = 7
After 7 days, the final state is [0,0,1,1,1,0,0,0]
.
If N
were much larger, we would detect when the state repeats, calculate the cycle length, and skip ahead using modulo.
The key insight for this problem is recognizing that the number of possible states is limited, so the process must eventually repeat, forming a cycle. By detecting cycles, we can fast-forward through many redundant steps, making it feasible to handle very large values of N
. This approach efficiently combines simulation with cycle detection, leveraging hash maps for quick lookups and modulo arithmetic for skipping ahead.
class Solution:
def prisonAfterNDays(self, cells, N):
seen = dict()
is_cycle = False
for i in range(N):
next_cells = [0]
for j in range(1, 7):
next_cells.append(1 if cells[j-1] == cells[j+1] else 0)
next_cells.append(0)
state_tuple = tuple(next_cells)
if state_tuple in seen:
# Cycle detected
cycle_length = i - seen[state_tuple]
remaining = (N - i - 1) % cycle_length
return self.prisonAfterNDays(next_cells, remaining)
else:
seen[state_tuple] = i
cells = next_cells
return cells
class Solution {
public:
vector<int> prisonAfterNDays(vector<int>& cells, int N) {
unordered_map<string, int> seen;
bool isCycle = false;
for (int i = 0; i < N; ++i) {
vector<int> next(8, 0);
for (int j = 1; j < 7; ++j) {
next[j] = (cells[j - 1] == cells[j + 1]) ? 1 : 0;
}
string key(next.begin(), next.end());
if (seen.count(key)) {
int cycle = i - seen[key];
int rem = (N - i - 1) % cycle;
return prisonAfterNDays(next, rem);
} else {
seen[key] = i;
}
cells = next;
}
return cells;
}
};
class Solution {
public int[] prisonAfterNDays(int[] cells, int N) {
Map<String, Integer> seen = new HashMap<>();
for (int i = 0; i < N; ++i) {
int[] next = new int[8];
for (int j = 1; j < 7; ++j) {
next[j] = cells[j - 1] == cells[j + 1] ? 1 : 0;
}
String key = Arrays.toString(next);
if (seen.containsKey(key)) {
int cycle = i - seen.get(key);
int rem = (N - i - 1) % cycle;
return prisonAfterNDays(next, rem);
} else {
seen.put(key, i);
}
cells = next;
}
return cells;
}
}
var prisonAfterNDays = function(cells, N) {
let seen = new Map();
for (let i = 0; i < N; ++i) {
let next = Array(8).fill(0);
for (let j = 1; j < 7; ++j) {
next[j] = cells[j - 1] === cells[j + 1] ? 1 : 0;
}
let key = next.join('');
if (seen.has(key)) {
let cycle = i - seen.get(key);
let rem = (N - i - 1) % cycle;
return prisonAfterNDays(next, rem);
} else {
seen.set(key, i);
}
cells = next;
}
return cells;
};