【753. 破解保险箱】

文章描述了一个需要通过特定密码打开的保险箱问题,其中密码是基于k位序列构建的n位数。解决方案利用了Hierholzer算法在欧拉图中找到欧拉回路的方法,以此生成能打开保险箱的最短字符串。代码实现采用C++,并分析了时间复杂度和空间复杂度。
摘要由CSDN通过智能技术生成

来源:力扣(LeetCode)

描述:

有一个需要密码才能打开的保险箱。密码是 n 位数, 密码的每一位是 k 位序列 0, 1, ..., k-1 中的一个 。

你可以随意输入密码,保险箱会自动记住最后 n 位输入,如果匹配,则能够打开保险箱。

举个例子,假设密码是 "345",你可以输入 "012345" 来打开它,只是你输入了 6 个字符.

请返回一个能打开保险箱的最短字符串。

示例1:

输入: n = 1, k = 2
输出: "01"
说明: "10"也可以打开保险箱。

示例2:

输入: n = 2, k = 2
输出: "00110"
说明: "01100", "10011", "11001" 也能打开保险箱。

提示:

  • n 的范围是 [1, 4]。
  • k 的范围是 [1, 10]。
  • kn 最大可能为 4096。

方法: Hierholzer 算法

Hierholzer 算法可以在一个欧拉图中找出欧拉回路。

具体地,我们将所有的 n − 1 位数作为节点,共有 kn−1 个节点,每个节点有 kk 条入边和出边。如果当前节点对应的数为 a1 a2 ⋯ an−1,那么它的第 x 条出边就连向数 a2 ⋯ an−1x 对应的节点。这样我们从一个节点顺着第 x 条边走到另一个节点,就相当于输入了数字 x。

在某个节点对应的数的末尾放上它的某条出边的编号,就形成了一个 n 位数,并且每个节点都能用这样的方式形成 k 个 n 位数。

例如 k = 4,n = 3 时,节点分别为 00, 01, 02, ⋯, 32, 33,每个节点的出边的编号分别为 0, 1, 2, 3,那么 00 和它的出边形成了 000, 001, 002, 003 这 4 个 3 位数,32 和它的出边形成了 320, 321, 322, 323 这 4 个 3 位数。

这样共计有 kn−1 × k = kn 个 n 位数,恰好就是所有可能的密码。

由于这个图的每个节点都有 k 条入边和出边,因此它一定存在一个欧拉回路,即可以从任意一个节点开始,一次性不重复地走完所有的边且回到该节点。因此,我们可以用 Hierholzer 算法找出这条欧拉回路:

设起始节点对应的数为 uu,欧拉回路中每条边的编号为 x1, x2, x3, ⋯,那么最终的字符串即为

u x 1x 2x 3

Hierholzer 算法如下:

  • 我们从节点 u 开始,任意地经过还未经过的边,直到我们「无路可走」。此时我们一定回到了节点 u,这是因为所有节点的入度和出度都相等。
  • 回到节点 u 之后,我们得到了一条从 u 开始到 u 结束的回路,这条回路上仍然有些节点有未经过的出边。我么从某个这样的节点 v 开始,继续得到一条从 v 开始到 v 结束的回路,再嵌入之前的回路中,即
u → ⋯ → v → ⋯ → u

变为

u → ⋯ → v → ⋯ → v → ⋯ → u

以此类推,直到没有节点有未经过的出边,此时我们就找到了一条欧拉回路。

实际的代码编写具有一定的技巧性。

代码:

class Solution {
private:
    unordered_set<int> seen;
    string ans;
    int highest;
    int k;

public:
    void dfs(int node) {
        for (int x = 0; x < k; ++x) {
            int nei = node * 10 + x;
            if (!seen.count(nei)) {
                seen.insert(nei);
                dfs(nei % highest);
                ans += (x + '0');
            }
        }
    }

    string crackSafe(int n, int k) {
        highest = pow(10, n - 1);
        this->k = k;
        dfs(0);
        ans += string(n - 1, '0');
        return ans;
    }
};

执行用时:4 ms, 在所有 C++ 提交中击败了86.44%的用户
内存消耗:9 MB, 在所有 C++ 提交中击败了30.51%的用户
复杂度分析
时间复杂度:O(n×kn )。
空间复杂度:O(n×kn )。
author:LeetCode-Solution

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千北@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值