8 间牢房排成一排,每间牢房不是有人住就是空着。
每天,无论牢房是被占用或空置,都会根据以下规则进行更改:
- 如果一间牢房的两个相邻的房间都被占用或都是空的,那么该牢房就会被占用。
- 否则,它就会被空置。
(请注意,由于监狱中的牢房排成一行,所以行中的第一个和最后一个房间无法有两个相邻的房间。)
我们用以下方式描述监狱的当前状态:如果第 i
间牢房被占用,则 cell[i]==1
,否则 cell[i]==0
。
根据监狱的初始状态,在 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 == 8
cells[i]
的值为0
或1
1 <= N <= 10^9
解法
显然,从第1天开始,数组的头尾永远都是0,中间的6位则可以由左右两位同或得到。又注意到cells
数组的长度永远是8,于是我想到用字节来存储每天的状态,然后专门写一个函数adjustByte(byte arrByte)来计算下一天对应的字节。显然,对于固定的输入,adjustByte(byte arrByte)函数会给出固定的输出,所以一旦发现计算出了重复的结果,就说明出现了循环。可以用一个ArrayList记录状态的出现顺序,用一个布尔数组记录每种状态是否已经出现过。由于头尾固定为0,状态至多有26=64种;不过为方便起见我还是使用了长度为8的布尔数组。
需要注意的是,并不是所有出现过的状态都在循环节中,在循环出现之前可能有一部分状态只会出现一次,因此在发现循环之后要注意对这一部分进行处理。
Leetcode的评论区许多人提到循环节长度固定为14的倍数,经测试确实如此。暂时不知道如何将这一规律推导出来,当然只为了减小运行时间的话归纳出了直接用就是了……
代码
1 import java.util.ArrayList; 2 import java.util.List; 3 4 /** 5 * 6 * @author yuan 7 * @version 0.1 8 * @date 2019/3/18 9 */ 10 public class Leetcode957 { 11 12 private static byte adjustByte(byte arrByte) { 13 // 隔两位异或 14 arrByte ^= (arrByte << 2); 15 // 取反得到同或值 16 arrByte = (byte) ~arrByte; 17 // 无符号左移一位使高位清零 &255是为了处理类型提升为int的问题 18 arrByte = (byte) ((arrByte & 255) >>> 1); 19 // 最低位清零 20 arrByte &= ~1; 21 return arrByte; 22 } 23 24 public static int[] prisonAfterNDays(int[] cells, int n) { 25 // 标记状态是否已经出现过 26 boolean[] map = new boolean[256]; 27 // 按顺序存储出现的状态 28 List<Integer> list = new ArrayList<>(); 29 // 用于表示状态的字节 30 byte arrByte = 0; 31 // 将数组转为字节 32 for (int i = 0; i < 8; i++) { 33 arrByte |= cells[i] << (7 - i); 34 } 35 // 按天计算 36 for (int i = 0; i < n; i++) { 37 // 出现重复,即出现循环 &0xff是为了将int转为无符号byte 38 if (map[arrByte & 0xff]) { 39 // 获取循环开始下标 40 int loopStart = list.indexOf((int) arrByte); 41 // 计算循环节长度 42 int loopLength = list.size() - loopStart; 43 // 计算非循环部分长度 44 int nonLoop = list.size() - loopLength; 45 // 除去非循环部分后,从循环部分中获取正确的byte 先强转int拆箱再强转byte 46 arrByte = (byte) (int) list.get((n - nonLoop) % loopLength + nonLoop); 47 break; 48 } 49 map[arrByte & 0xff] = true; 50 list.add((int) arrByte & 0xff); 51 arrByte = adjustByte(arrByte); 52 } 53 // 结果数组 54 int[] result = new int[8]; 55 // 将状态字节转为数组 56 for (int i = 0; i < 8; i++) { 57 result[i] = arrByte >> (7 - i) & 1; 58 } 59 return result; 60 } 61 }
坑点
java自动类型提升问题+符号位问题
运行结果
AC 21 ms 42.3 MB