题目地址:
https://leetcode.com/problems/prison-cells-after-n-days/
给定一个长度是 8 8 8的 0 − 1 0-1 0−1数组 A A A,对于每个 i i i,如果 A [ i − 1 ] = A [ i + 1 ] A[i-1]=A[i+1] A[i−1]=A[i+1],那么下一天 A [ i ] A[i] A[i]就会变成 1 1 1,否则其会变成 0 0 0。问过了 N N N天之后 A A A会变成什么样。对于 A [ 0 ] A[0] A[0]和 A [ 7 ] A[7] A[7],由于其只有一个邻居,所以规定过了一天它们就会变为 0 0 0。
可以用个
8
8
8位整数的二进制位来表示这个数组。设当前状态是s
,那么过了一天之后,状态就变为了~((s << 1) ^ (s >> 1)) & 0X7E
,后面要与上0X7E
是为了将首尾清零。由于一共最多有
2
8
=
256
2^8=256
28=256种状态(事实上由于过了一天
A
[
0
]
A[0]
A[0]和
A
[
7
]
A[7]
A[7]两个位置都是
0
0
0了,其实没有这么多),所以过了足够多天之后,一定会产生循环。如果还没来得及产生循环的时候就到了第
N
N
N天,那么可以直接返回答案。否则的话,我们可以缓存一下每个状态是第几天到达的(初始状态视为是第
0
0
0天到达的),一旦发现了循环,算出循环节长度
l
l
l,并查缓存得出循环节开始状态是第几天(设为是第
c
c
c天。循环节开始状态的意思是,第一次发现的之前访问过的状态),那么答案就是第
(
N
−
c
)
m
o
d
l
+
c
(N-c)\mod l+c
(N−c)modl+c天的状态,再查一下缓存看一下那一天状态是什么,返回之即可。代码如下:
import java.util.Arrays;
public class Solution {
public int[] prisonAfterNDays(int[] cells, int N) {
int[] cache = new int[1 << 8];
Arrays.fill(cache, -1);
// 求出初始状态
int cur = 0;
for (int i = 7; i >= 0; i--) {
if (cells[i] == 1) {
cur |= 1 << (7 - i);
}
}
// 初始状态视为是第0天
cache[cur] = 0;
// len存循环节长度
int len = 0;
for (int i = 1; i <= N; i++) {
// 算出第i天的状态
cur = ~((cur << 1) ^ (cur >> 1)) & 0X7E;
// 如果第i天的状态之前访问过,那么得出循环节长度
if (cache[cur] != -1) {
len = i - cache[cur];
break;
} else {
// 否则记录一下第一次访问到cur这个状态是第几天
cache[cur] = i;
}
}
// 如果循环节长度是0,意味着还没产生循环,直接返回cur对应的状态
if (len == 0) {
return get(cur);
}
// 否则cache[cur]就是循环节开始的天数,我们算一下第N天的状态等价于第几天的状态
int idx = (N - cache[cur]) % len + cache[cur];
// 找到那天的状态并返回
for (int i = 0; i < 1 << 8; i++) {
if (cache[i] == idx) {
return get(i);
}
}
return null;
}
private int[] get(int cur) {
int[] res = new int[8];
int idx = 7;
while (cur != 0) {
res[idx--] = cur & 1;
cur >>= 1;
}
return res;
}
}
时空复杂度 O ( 1 ) O(1) O(1)。