# 681. Next Closest Time

## 681. Next Closest Time

Given a time represented in the format “HH:MM”, form the next closest time by reusing the current digits. There is no limit on how many times a digit can be reused.

You may assume the given input string is always valid. For example, “01:34”, “12:09” are all valid. “1:34”, “12:9” are all invalid.

```
Example 1:
Input: "19:34"
Output: "19:39"
Explanation: The next closest time choosing from digits 1, 9, 3, 4, is 19:39, which occurs 5 minutes later.  It is not 19:33, because this occurs 23 hours and 59 minutes later.1234
```

```
Example 2:
Input: "23:59"
Output: "22:22"
Explanation: The next closest time choosing from digits 2, 3, 5, 9, is 22:22. It may be assumed that the returned time is next day's time since it is smaller than the input time numerically.1234
```

***My Solutions：***

&#x20;这道题给了一个时间点，求最近的下一个时间点，规定了不能产生新的数字，当下个时间点超过零点时，就当第二天的时间。

为了找到下一个时间点，从分钟开始换数字，而且换的数字要是存在的数字，那么先要做的就是统计当前时间点中的数字，由于可能有重复数字的存在，用一个伪哈希表来存给的时间的数字。

下面就从低位分钟开始换数字了，如果低位分钟上的数字已经是最大的数字了，那么说明要转过一轮了，就要把低位分钟上的数字换成最小的那个数字；否则就将低位分钟上的数字换成下一个数字。换一种说法就是，去哈希表里找有没有比给定的这个数大而且合法的数字，如果有则返回带有这个合法数字的新的string；如果没有，就把这一位设为哈希表里的最小值。然后再看高位分钟上的数字，同理，如果高位分钟上的数字已经是最大的数字，或则下一个数字大于5，那么直接换成最小值；否则就将高位分钟上的数字换成下一个数字。

对于小时位上的数字也是同理。当小时高位不为2的时候，低位可以是0\~9，而当高位为2时，低位需要小于等于3。对于小时高位，其必须要小于等于2。

时间复杂度: O(4\*10)

空间复杂度: O(10)

```
public String nextClosestTime(String time) {
        char[] t = time.toCharArray(), result = new char[4];
        int[] list = new int[10]; //数字集合
        char min = '9'; //记录最小的数
        
        for (char c : t) {
            if (c == ':') continue;
            list[c - '0']++;
            if (c < min) {
                min = c;
            }
        }
        
        //分钟最低位，如果有比最低位大的数字，直接返回
        for (int i = t[4] - '0' + 1; i <= 9; i++) {
            if (list[i] != 0) {
                t[4] = (char)(i + '0');
                return new String(t);
            }
        }
        //如果没有，则把第四位设为最小值
        t[4] = min;
        
        //分钟高位，如果有比最低位大的数字且此数字<=5，直接返回
        for (int i = t[3] - '0' + 1; i <= 5; i++) {
            if (list[i] != 0) {
                t[3] = (char)(i + '0');
                return new String(t);
            }
        }
        //如果没有，则把第三位设为最小值
        t[3] = min;
        
        //检查时钟第一位是0/1 还是2。如果是0/1,stop是9，因为最多到09或19；不然stop是3，最多到23
        int stop = t[0] < '2' ? 9 : 3;
        
        for (int i = t[1] - '0' + 1; i <= stop; i++) {
            if (list[i] != 0) {
                t[1] = (char)(i + '0');
                return new String(t);
            }
        }
        t[1] = min;
        
        for (int i = t[0] - '0' + 1; i <= 2; i++) {
            if (list[i] != 0) {
                t[0] = (char)(i + '0');
                return new String(t);
            }
        }
        t[0] = min;
        
        return new String(t);
}
```

&#x20;原题 -> closest time after + 不可重复使用数字 -> closest time before + （可重复使用数字 or 不可重复使用数字）

Follow Up 1:

如果是closest time after + 不可重复使用数字。

e.g，

19：34 ==> 19:43，情况1

16：29 ==> 19:26，情况2

23：26 ==> 22:36 next day，情况2

15：58 ==> 18:58 ，情况2

22：23 ==> 23:22，情况2

12：49 ==> 21:49，情况3

19：29 ==> 19:29

23：55 ==> 23:55

先看分钟，如果可以只靠移动分钟数改变最简单。需要满足的条件是十位数小于个位数且个位数<=5。不然的话必然需要移动时钟数。--情况1

再看时钟。如果第一个是0/1，低位数最多是9；如果第一个是2，低位数最多是3。如果分钟数两数中，有比时钟低位数大的数，且分钟数小于等于9/3, 选择分钟数里较小的数放在低位。剩下两个数小一点的数必须小于等于5。--情况2

如果不满足以上条件，但是时钟高位是1，且后面的数字里有2的，以及剩余两个数字有一个小于等于5的，时钟变成21， 剩余数字按大小排列。 --情况3

剩余情况无法变化，是24小时候的本身。

Follow Up 2:

如果是closest time before + 可重复使用数字, 比如 12：34，答案是12：33。

下面就从低位分钟开始换数字了，如果低位分钟上的数字已经是最小的数字了，那么说明要回转过一轮了，就要把低位分钟上的数字换成最大的那个数字；否则就将低位分钟上的数字换成次小的数字。换一种说法就是，去哈希表里找有没有比给定的这个数小而且合法的数字，如果有则返回带有这个合法数字的新的string；如果没有，就把这一位设为哈希表里的最大值。然后再看高位分钟上的数字，同理，如果高位分钟上的数字已经是最小的数字，或则下一个数字大于5，那么直接换成最小值；否则就将高位分钟上的数字换成下一个数字。

对于小时位上的数字也是同理。当小时高位不为2的时候，低位可以是0\~9，而当高位为2时，低位需要小于等于3。对于小时高位，其必须要小于等于2。

```
public String nextClosestTime(String time) {
        char[] t = time.toCharArray(), result = new char[4];
        int[] list = new int[10]; //数字集合，0~9
        char max = '0'; //记录最大的数
        
        for (char c : t) {
            if (c == ':') continue;
            list[c - '0']++;
            if (c > max) {
                max = c;
            }
        }
        
        //分钟最低位，如果有比最低位小的数字，直接返回。eg，12：34变成12：33
        for (int i = t[4] - '0' - 1; i >= 0; i--) {
            if (list[i] != 0) {
                t[4] = (char)(i + '0');
                return new String(t);
            }
        }
        //如果没有，则把第四位设为最大值
        t[4] = max;
        
        //分钟高位，如果有比最低位小的数字且此数字<=5，直接返回。
        //eg, 22：21上一步变成22：22，这一步变成22：12
        for (int i = t[3] - '0' - 1; i >= 0 && i <= 5; i--) {
            if (list[i] != 0) {
                t[3] = (char)(i + '0');
                return new String(t);
            }
        }
        //如果没有，则把第三位设为最大值
        t[3] = max;
        
        //检查时钟第一位是0/1 还是2。如果是0/1,stop是9，因为时钟位最多到09或19；不然stop是3，最多到23
        int stop = t[0] < '2' ? 9 : 3;
        
         //时钟最低位，如果有比此位数小的数字，直接返回
         //eg, 21：11第一步变成21：12，第二步变成21：22，这一步变成22：22
        for (int i = t[1] - '0' - 1; i >= 0 && i <= stop; i--) {
            if (list[i] != 0) {
                t[1] = (char)(i + '0');
                return new String(t);
            }
        }
        t[1] = max;
        
        //时钟高位，如果有比此位数小的数字，直接返回
         //eg, 21：11第一步变成21：12，第二步变成21：22，第三步变成22：22，这一步变成12：22
        for (int i = t[0] - '0' + 1; i >= 0 && i <= 2; i--) {
            if (list[i] != 0) {
                t[0] = (char)(i + '0');
                return new String(t);
            }
        }
        t[0] = max;
        
        return new String(t);
}
```
