从0开始的秋招刷题路,记录下所刷每道题的题解,帮助自己回顾总结
753. 破解保险箱
有一个需要密码才能打开的保险箱。密码是 n 位数, 密码的每一位都是范围 [0, k - 1] 中的一个数字。
保险箱有一种特殊的密码校验方法,你可以随意输入密码序列,保险箱会自动记住 最后 n 位输入 ,如果匹配,则能够打开保险箱。
例如,正确的密码是 “345” ,并且你输入的是 “012345” :
输入 0 之后,最后 3 位输入是 “0” ,不正确。
输入 1 之后,最后 3 位输入是 “01” ,不正确。
输入 2 之后,最后 3 位输入是 “012” ,不正确。
输入 3 之后,最后 3 位输入是 “123” ,不正确。
输入 4 之后,最后 3 位输入是 “234” ,不正确。
输入 5 之后,最后 3 位输入是 “345” ,正确,打开保险箱。
在只知道密码位数 n 和范围边界 k 的前提下,请你找出并返回确保在输入的 某个时刻 能够打开保险箱的任一 最短 密码序列 。
示例 1:
输入:n = 1, k = 2
输出:“10”
解释:密码只有 1 位,所以输入每一位就可以。“01” 也能够确保打开保险箱。
示例 2:
输入:n = 2, k = 2
输出:“01100”
解释:对于每种可能的密码:
- “00” 从第 4 位开始输入。
- “01” 从第 1 位开始输入。
- “10” 从第 3 位开始输入。
- “11” 从第 2 位开始输入。
因此 “01100” 可以确保打开保险箱。“01100”、“10011” 和 “11001” 也可以确保打开保险箱。
提示:
1 <= n <= 4
1 <= k <= 10
1 <=
k
n
k^n
kn <= 4096
解法一: 欧拉回路
前置知识: 欧拉回路:通过图中所有边一次且仅一次行遍所有顶点的回路称为欧拉回路
首先题目要求能打开保险箱的最短字符串,即将n位所有可能的排列组合都包含进去,例如00110,k = 2, n = 2,那么包含的可能位00,01,11,10将n位k进制所有的可能都包含进去因此一定能够成果试出密码。那么题目便转化为求它的包含所有排列组合的字符串。
由于每次只取最后一个字符来进行移动,例如n=3,k=2时,那么对于000,可以转化为000,001, 对于011,可以转化为110,111。根据观察,我们可以将每个状态抽象为1个点,即对于n位密码,我们将n−1的可能组合看作一个点,那么对于n=3,k=2来说包含点={00,01,10,11}
时间复杂度:
O
(
k
n
)
O(k^n)
O(kn),对于每个结点都有k条边,一共有
k
n
−
1
k^{n-1}
kn−1个结点。
空间复杂度:
O
(
k
n
)
O(k^n)
O(kn),为边数
class Solution {
StringBuilder sb = new StringBuilder();
int n, k, mod;
Set<Integer> vis = new HashSet<>(); //判断每条边是否访问过
public String crackSafe(int N, int K) {
n = N; k = K; mod = (int)Math.pow(10, n - 1);
dfs(0); //从0点出发
sb.append("0".repeat(n - 1));
return sb.toString();
}
void dfs(int u) {
for (int i = 0; i < k; i++) { //对于每个点循环k条边即可
int v = u * 10 + i; //即边的编号
if (vis.contains(v)) continue;
vis.add(v); //标记该边已经访问
dfs(v % mod); //去掉第一位为结点编号
sb.append(i); //添加最后一个数字
}
}
}