Medium
题目描述
一排有 8 个牢房,每个牢房要么被占用,要么空着。
每天,根据以下规则,牢房的占用状态会发生变化:
- 如果一个牢房的两个相邻牢房都被占用或都是空的,那么这个牢房就会被占用。
- 否则,它就会空着。
注意,由于监狱是一排,第一个和最后一个牢房不能有两个相邻的牢房。
给你一个整数数组 cells,其中 cells[i] == 1 表示第 i 个牢房被占用,cells[i] == 0 表示第 i 个牢房是空的,以及一个整数 n。
返回 n 天后监狱的状态(即经过上述 n 次变化后的状态)。
示例 1:
输入:cells = [0,1,0,1,1,0,0,1], n = 7
输出:[0,0,1,1,0,0,0,0]
解释:下表总结了监狱每天的状态:
Day 0: [0, 1, 0, 1, 1, 0, 0, 1]
Day 1: [0, 1, 1, 0, 0, 0, 0, 0]
Day 2: [0, 0, 0, 0, 1, 1, 1, 0]
Day 3: [0, 1, 1, 0, 0, 1, 0, 0]
Day 4: [0, 0, 0, 0, 0, 1, 0, 0]
Day 5: [0, 1, 1, 1, 0, 1, 0, 0]
Day 6: [0, 0, 1, 0, 1, 1, 0, 0]
Day 7: [0, 0, 1, 1, 0, 0, 0, 0]
示例 2:
输入:cells = [1,0,0,1,0,0,1,0], n = 1000000000
输出:[0,0,1,1,1,1,1,0]
提示:
cells.length == 8cells[i]要么是0,要么是11 <= n <= 10^9
解题思路
这道题的关键在于观察状态变化的周期性。由于牢房只有8个,每个牢房只有0或1两种状态,所以总共只有 2^8 = 256 种可能的状态。因此状态变化必然会形成周期。
解题思路:
模拟状态转换:首先实现状态转换逻辑,注意第一个和最后一个牢房总是变为0(因为它们没有两个相邻牢房)。
寻找周期:从第一天开始记录每天的状态,当遇到重复状态时,说明找到了周期。由于初始状态可能不在周期内,需要特别处理。
优化计算:一旦找到周期,就可以通过取模运算直接计算第n天的状态,避免逐天模拟。
具体实现:
- 使用哈希表记录每个状态第一次出现的天数
- 当发现重复状态时,计算周期长度
- 使用
(n - start) % cycle来确定最终状态在周期中的位置
这种方法特别适合处理n很大的情况,时间复杂度从O(n)优化到O(周期长度),通常周期长度很小。
代码实现
class Solution {
public:
vector<int> prisonAfterNDays(vector<int>& cells, int n) {
if (n == 0) return cells;
unordered_map<string, int> seen;
vector<vector<int>> history;
vector<int> current = cells;
for (int day = 1; day <= n; day++) {
vector<int> next(8, 0);
for (int i = 1; i < 7; i++) {
if (current[i-1] == current[i+1]) {
next[i] = 1;
}
}
current = next;
string state = "";
for (int cell : current) {
state += to_string(cell);
}
if (seen.count(state)) {
int cycleStart = seen[state];
int cycleLength = day - cycleStart;
int targetDay = cycleStart + (n - cycleStart) % cycleLength;
return history[targetDay - 1];
}
seen[state] = day;
history.push_back(current);
}
return current;
}
};
class Solution:
def prisonAfterNDays(self, cells: List[int], n: int) -> List[int]:
if n == 0:
return cells
seen = {}
history = []
current = cells[:]
for day in range(1, n + 1):
next_state = [0] * 8
for i in range(1, 7):
if current[i-1] == current[i+1]:
next_state[i] = 1
current = next_state
state = tuple(current)
if state in seen:
cycle_start = seen[state]
cycle_length = day - cycle_start
target_day = cycle_start + (n - cycle_start) % cycle_length
return history[target_day - 1]
seen[state] = day
history.append(current[:])
return current
public class Solution {
public int[] PrisonAfterNDays(int[] cells, int n) {
if (n == 0) return cells;
Dictionary<string, int> seen = new Dictionary<string, int>();
List<int[]> history = new List<int[]>();
int[] current = (int[])cells.Clone();
for (int day = 1; day <= n; day++) {
int[] next = new int[8];
for (int i = 1; i < 7; i++) {
if (current[i-1] == current[i+1]) {
next[i] = 1;
}
}
current = next;
string state = string.Join("", current);
if (seen.ContainsKey(state)) {
int cycleStart = seen[state];
int cycleLength = day - cycleStart;
int targetDay = cycleStart + (n - cycleStart) % cycleLength;
return history[targetDay - 1];
}
seen[state] = day;
history.Add((int[])current.Clone());
}
return current;
}
}
var prisonAfterNDays = function(cells, n) {
if (n === 0) return cells;
const seen = new Map();
let current = [...cells];
for (let day = 1; day <= n; day++) {
const key = current.join('');
if (seen.has(key)) {
const cycleStart = seen.get(key);
const cycleLength = day - cycleStart;
const remaining = (n - cycleStart) % cycleLength;
for (let i = 0; i < remaining; i++) {
current = getNextDay(current);
}
return current;
}
seen.set(key, day);
current = getNextDay(current);
}
return current;
};
function getNextDay(cells) {
const next = new Array(8).fill(0);
for (let i = 1; i < 7; i++) {
if (cells[i-1] === cells[i+1]) {
next[i] = 1;
}
}
return next;
}
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(min(n, 2^6)) = O(min(n, 64)),最多64种不同状态(去除首尾固定为0的位置) |
| 空间复杂度 | O(min(n, 2^6)) = O(min(n, 64)),用于存储状态历史和哈希表 |