leetcode55java_Leetcode-957 N 天后的牢房 Java 详细注释

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 importjava.util.ArrayList;2 importjava.util.List;3

4 /**

5 *6 *@authoryuan7 *@version0.18 * @date 2019/3/189 */

10 public classLeetcode957 {11

12 private static byte adjustByte(bytearrByte) {13 //隔两位异或

14 arrByte ^= (arrByte << 2);15 //取反得到同或值

16 arrByte = (byte) ~arrByte;17 //无符号左移一位使高位清零 &255是为了处理类型提升为int的问题

18 arrByte = (byte) ((arrByte & 255) >>> 1);19 //最低位清零

20 arrByte &= ~1;21 returnarrByte;22 }23

24 public static int[] prisonAfterNDays(int[] cells, intn) {25 //标记状态是否已经出现过

26 boolean[] map = new boolean[256];27 //按顺序存储出现的状态

28 List 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 returnresult;60 }61 }

坑点

java自动类型提升问题+符号位问题

运行结果

AC 21 ms 42.3 MB

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值