信号灯亮灭问题

题目描述

描述


二战时德军某部加密设备上有8个一组的信号灯,这些信号灯的亮灭用于初始化加密机的密钥。
这些信号灯每秒钟变化一次,并且灯的某实可状态完全由上一秒钟灯组的状态决定,具体的规则为:
如果一个信号灯左右相邻的灯都亮,或者都灭,则下一秒该灯亮。其他情况下,信号灯都灭。(特别地,比如两边的灯,他们只有一个相邻灯)
这里我们以字符串ON代表灯亮,以字符串OFF代表灯灭。给定一组灯初始状态,以及经过的时间秒数s,求s秒后每个灯的亮灭情况。
注意,需考虑s非常大的情况,全遍历(时间复杂度O(n))算法通过的用例很少。

输入描述

灯最开始的状态

OFF,OFF,OFF,OFF,ON,ON,ON,OFF,OFF   

经过的时间

5


输出描述


灯经过5秒后的状态:
OFF,OFF,ON,ON,OFF,OFF,OFF,OFF

解释:
根据规则
1秒后灯的状态OFF,ON,ON,OFF,OFF,ON,OFF,OFF
2秒后灯的状态OFF,OFF,OFF,OFF,OFF,ON,OFF,OFF
3秒后灯的状态OFF,ON,ON,ON,OFF,ON,OFF,OFF
4秒后灯的状态OFF,OFF,ON,OFF,ON,ON,OFF,OFF
5秒后灯的状态OFF,OFF,ON,ON,OFF,OFF,OFF,OFF

题目分析

初步分析

题目中说明了全遍历通过率低,那么我们放弃暴力破解,分析一下题目。

由题意可知,两头的灯永远是灭的,那么8盏灯存在的可能情况,共有2^{6}=64种情况。这里用1、0表示灯的亮灭。

在灯的变化中,由A状态一定会变为B状态,B变为C,如果在变化中,A再次出现,那么 A -> B -> C -> …… -> A 组成了一个循环。

例如 0 1 1 0 (允许我用少一点的分析,清晰一些),1~2秒构成了一个循环。

0秒后 0 1 1 0
1秒后 0 0 0 0
2秒后 0 1 1 0

这样可以用动态规划的思想,把出现过的情况都存储起来,最坏情况下会出现64种。此时我们的复杂度为O(1)。

后续分析

然后我们再仔细分析一下,会不会出现 A -> B -> C -> D -> …… -> Z -> C 的情况,即 C -> Z 组成了一个循环,A、B不在循环内呢。

当然是有可能的啦,比如这个例子 0 0 1 1 0 。

0秒后 0 0 1 1 0
1秒后 0 0 0 0 0
2秒后 0 1 1 1 0
3秒后 0 0 1 0 0
4秒后 0 0 1 0 0

我们可以看到,3秒之后 0 0 1 0 0形成了一个循环,而0~2秒的状态不进入循环。所以这种状态是需要考虑滴。

详细分析

假设位置 j 开始循环,位置 i-1 结束循环,即 i 与 j 位置的字符串相同

那么

不计入循环 : 0   ~   ( j - 1)      长度 j

循环体:         j     ~   ( i - 1)      长度 i - j

 

S 秒后的位置 :  ( s - j ) % ( i - j )  + j

描述:  先减去不计入循环的      除以循环长度   加上循环开始位置

代码


import java.util.HashMap;

public class LightEncode {
    public static void main(String[] args) {
        String str = "OFF,OFF,OFF,OFF,ON,ON,ON,OFF,OFF";
        System.out.println(test1(str, 5));
        System.out.println(test2(str, 5));
    }

    private static String test1(String str, Integer s) {
        String tmp = encode(str);
        //记录key-value
        HashMap<Integer, String> map1 = new HashMap<>();
        HashMap<String, Integer> map2 = new HashMap<>();
        int i = 1;
        map1.put(0, tmp);
        map2.put(tmp, 0);
        for (i = 1; i < s; i++) {
//            System.out.println(decode(tmp));
            tmp = getNext(tmp);
            if (map2.containsKey(tmp)) {
                // 在 i 出现了重复的字符串,所以 i-1为循环结束位置              ------------- i-1 循环结束位置
                break;
            } else {
                map1.put(i, tmp);
                map2.put(tmp, i);
            }
        }
        if (i==s) {
            //由于是从1开始的,少跑了一趟,在这里补上
            return decode(getNext(tmp));
        } else {
            // 开始循环的位置 j                                         ---------------  j 循环开始位置
            Integer j = map2.get(tmp);

            // 不计入循环    0 ~ (j-1)       长度 j
            // 循环         j ~ (i-1)       长度 i-j
            // s秒后的位置 -> (s - j)   %    (i - j) +    j
            //               ^               ^          ^
            //               |               |          |
            //      先减去不计入循环的      除以循环长度   加上循环开始位置
            return decode(map1.get((s-j) % (i-j) + j));
        }
    }

    private static String test2(String str, Integer s) {
        String tmp = encode(str);
        int i = 0;
        for (i = 0; i < s; i++) {
            System.out.println(decode(tmp));
            tmp = getNext(tmp);
        }
        return decode(tmp);
    }

    /**
     * 计算下个序列
     * @param str
     * @return
     */
    private static String getNext(String str) {
        String res = "0";
        for (int i = 1; i < 7; i++) {
            if (str.charAt(i-1) == str.charAt(i+1)) {
                res = res + "1";
            } else {
                res = res + "0";
            }
        }
        return res + "0";
    }

    private static String encode(String str) {
        String tmp = str.replaceAll("ON", "1").replaceAll("OFF", "0").replace(",", "");
        return tmp;
    }

    private static String decode(String str) {
        String tmp = str.replaceAll("0", "OFF,").replaceAll("1", "ON,");
        tmp = tmp.substring(0, tmp.length()-1);
        return tmp;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值