LeetCode 753. 破解保险箱【欧拉回路,DFS】困难

news2024/11/26 4:45:43

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

有一个需要密码才能打开的保险箱。密码是 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 <= 4096

保险箱的密码是一个长度为  n n n 的数字字符串,密码中每位数字的取值范围是从  0 0 0 到  k − 1 k-1 k1 。现在这  n n n 位密码未知,题目要求我们生成一个可以暴力破解这  n n n 位密码的字符串序列  s e q seq seq ,要想用  s e q seq seq 破解密码,那么  s e q seq seq 中必须包含  n n n 位密码的所有组合,最简单的是将  n n n 位密码的  k n k^n kn 个组合拼在一起构成  s e q seq seq ,但这样的  s e q seq seq 太长了,题目要我们生成一个包含  n n n 位密码所有组合情况的一个最短序列

举个例子, n = 2 , k = 2 n = 2, k = 2 n=2,k=2 ,密码长度为  2 2 2 ,每位数字由 0 0 0 和  1 1 1 组成,那么长度为  2 2 2 的密码就有  k n = 2 2 = 4 k^n = 2^2 = 4 kn=22=4 种组合,即  00 , 01 , 10 , 11 00, 01, 10, 11 00,01,10,11 ,要想破解密码,最简做法是,将这四种密码组合拼在一起组成破解序列  s e q = 00011011 seq = 00011011 seq=00011011 ,这样得到的序列长度为  8 8 8 ,但它不是包含所有密码组合的最短序列。也就是说这种简单拼接在一起的方法,得到的破解序列是冗余的,会增加破解时间。

而密码破解的方法其实就是朴素的字符串匹配,比如上面的  s e q = 00011011 seq = 00011011 seq=00011011 ,依次匹配的子串是  00 , 00 , 01 , 11 , 10 , 01 , 11 00, 00, 01, 11, 10, 01, 11 00,00,01,11,10,01,11 ,一共做了  7 7 7 次密码匹配,其中  00 , 01 , 11 00, 01, 11 00,01,11 分别匹配了两次,也就是说多做了  3 3 3 次冗余的密码匹配。题目要求的 最短破解序列 就是不会产生冗余密码匹配的序列,长度正好是  k n + ( n − 1 ) k^n + (n - 1) kn+(n1) ,最多只需要匹配  k n k^n kn 次 s e q = 00110 seq = 00110 seq=00110 是上面的其中一个破解序列,长度为  5 5 5 ,依次匹配的子串分别是  00 , 01 , 11 , 10 00, 01, 11, 10 00,01,11,10 ,匹配的正好是所有四种密码组合。

一种建模方式是:对于所有  m = k n m = k^n m=kn 个密码组合,我们把每个  n n n 位可能的密码看成是图中的一个顶点,对于这  m m m 个顶点构成的 完全图 中,让我们找到这样一个回路  v 1 → v 2 → v 3 → . . . → v m → v 1 v_1 \to v_2\to v_3\to ... \to v_m \to v_1 v1v2v3...vmv1除起始顶点外每个顶点访问且仅访问一次,其中  < u , v > <u, v> <u,v> 表示回路中一条由顶点 u u u 指向  v v v,且顶点  u u u 长为  n − 1 n-1 n1 的后缀恰好是顶点  v v v 的前缀。最终我们要的 最短破解序列 ,一种顶点序列是  v 1 , v 2 , v 3 , . . . , v m v_1, v_2, v_3, ..., v_m v1,v2,v3,...,vm ,长度为  k n + ( n − 1 ) k^n + (n - 1) kn+(n1) ,这个 最短破解序列 不止一个,因为 回路 中的任意一个顶点都可以做 起始顶点。这就是 哈密顿回路问题

不过查看Wiki,发现本题求的答案有专门的术语 De Bruijn sequence B ( k , n ) B(k,n) B(k,n) k k k 进制元素构成的循环序列,所有长度为 n n n k k k 进制元素序列都是 B ( k , n ) B(k, n) B(k,n) 的子数组(以环状形式),在 B ( k , n ) B(k,n) B(k,n) 中出现且仅出现一次。

描述该循环序列的图是 De Bruijn 图(一张欧拉图)。使用( n − 1 = 4 − 1 = 3 n - 1 = 4 - 1=3 n1=41=33-D De Bruijn 图可以循环构造长度为 2 4 = 16 2^4 = 16 24=16B(2,4) De Bruijn 序列。如下的3维 De Bruijn 图中的每条边对应于一个四位数字的序列:三个数字分别标记该边要离开的顶点,其后是一个数字标记该边。如果一个人从 000 000 000 穿过标记为 1 1 1 的边,则一个人到达 001 001 001 ,从而表明 De Bruijn 序列中存在子序列 0001 0001 0001精确遍历每条边一次,就是使用 16 16 16 个四位数序列中的每一个恰好一次。下图,在 B(2,3) 中每个顶点被访问一次,而在 B(2,4) 中每条边(包括自环)都被遍历一次。


解法 Hierholzer \text{Hierholzer} Hierholzer 算法

Hierholzer \text{Hierholzer} Hierholzer 算法可以在一个欧拉图中找出欧拉回路。具体地,我们将所有的 n − 1 n-1 n1 位数作为节点,共有 k n − 1 k^{n-1} kn1 个节点,每个节点有 k k k 条入边和出边。如果当前节点对应的数为 a 1 a 2 ⋯ a n − 1 a_1 a_2 \cdots a_{n-1} a1a2an1 ,那么它的第 x x x 条出边就连向数 a 2 ⋯ a n − 1 x a_2 \cdots a_{n-1} x a2an1x 对应的节点。这样从一个节点顺着第 x x x 条边走到另一个节点,就相当于输入了数字 x x x

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

例如 k = 4 , n = 3 k=4,n=3 k=4,n=3 时,节点分别为 00 , 01 , 02 , ⋯   , 32 , 33 00,01,02,⋯ ,32,33 00,01,02,,32,33 ,每个节点的出边的编号分别为 0 , 1 , 2 , 3 0,1,2,3 0,1,2,3 ,那么 00 00 00 和它的出边形成了 000 , 001 , 002 , 003 000,001,002,003 000,001,002,003 4 4 4 3 3 3 位数, 32 32 32 和它的出边形成了 320 , 321 , 322 , 323 320,321,322,323 320,321,322,323 4 4 4 3 3 3 位数。这样共计有 k n − 1 × k = k n k^{n-1} \times k = k^n kn1×k=kn n n n 位数,恰好就是所有可能的密码

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

  • 设起始节点对应的数为 u u u ,欧拉回路中每条边的编号为 x 1 , x 2 , x 3 , ⋯ x_1, x_2, x_3, \cdots x1,x2,x3, ,那么最终的字符串即为 u   x 1   x 2   x 3 ⋯   u u~ x_1 ~ x_2 ~ x_3 \cdots\ u u x1 x2 x3 u

H i e r h o l z e r Hierholzer Hierholzer 算法如下:

  1. 我们从节点 u u u 开始,任意地经过还未经过的边,直到我们「无路可走」。此时我们一定回到了节点 u u u ,这是因为所有节点的入度和出度都相等。
  2. 回到节点 u u u 之后,我们得到了一条从 u u u 开始到 u u u 结束的回路,这条回路上仍然有些节点有未经过的出边。从某个这样的节点 v v v 开始,继续得到一条从 v v v 开始到 v v v 结束的回路,再嵌入之前的回路中,即 u → ⋯ → v → ⋯ → u u \to \cdots \to v \to \cdots \to u uvu
    变为 u → ⋯ → v → ⋯ → v → ⋯ → u u\to \cdots \to v \to \cdots \to v \to \cdots \to u uvvu
  3. 以此类推,直到没有节点有未经过的出边,此时我们就找到了一条欧拉回路。

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

class Solution {
private:
    int highest, k;
    string ans;
    unordered_set<int> rec;
    void dfs(int node) {
        for (int x = 0; x < k; ++x) {
            int nei = node * 10 + x;
            if (!rec.count(nei)) { // 改为插入边
                rec.insert(nei);
                dfs(nei % highest);
                ans += (x + '0');
            }
        }
    }
public:
    string crackSafe(int n, int k) {
        this->highest = pow(10, n - 1);
        this->k = k;
        dfs(0); // 从n-1个0出发
        ans += string(n - 1, '0'); // 返回的路径顺序应该是n-1个0+翻转的ans
        // 由于是欧拉回路,所以不用翻转,即可直接返回
        return ans;
    }
};

但上述写法比较抽象。我们可以这么写:从一个 n − 1 n-1 n1 长度的全 0 0 0 串出发枚举 k k k 条边,每经过一条边就将其加入到哈希表中,防止重复经过同一条边。

class Solution {
private:
    string ans;
    unordered_set<string> edges;
    void dfs(string &cur, int k) {
        for (int i = 0; i < k; ++i) {
            string edge = cur + to_string(i);
            if (!edges.count(edge)) {
                edges.insert(edge);
                string next = edge.substr(1);
                dfs(next, k);
                ans += to_string(i);
            }
        }
    }
public:
    string crackSafe(int n, int k) {
        string start = string(n - 1, '0');
        dfs(start, k);
        ans += start;
        return ans;
    }
};

复杂度分析:

  • 时间复杂度: O ( n × k n ) O(n \times k^n) O(n×kn)
  • 空间复杂度: O ( n × k n ) O(n \times k^n) O(n×kn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1029298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实现AIGC更好的数据存力,这家科技巨头为我们指明了方向

存力即数据存储能力 蕴藏着巨大的发展机会 【全球存储观察 &#xff5c; 热点关注】 2023年&#xff0c;全球被ChatGPT的热潮席卷&#xff0c;拥抱AIGC的创新赛道成为众多企业的新选择。 全球存储观察分析指出&#xff0c;影响AIGC发展的三大因素也日益凸显&#xff0c;即算…

ROS之创建第一个程序

打开终端 创建工作空间 mkdir ros_ws进入工作空间 cd ros_ws创建src文件夹&#xff08;放源程序&#xff09; mkdir src编译工作空间 catkin_make打开vscode&#xff08;从终端打开此工程&#xff09; code .进入src文件夹 cd src创建功能包demo1&#xff0c;并加载依赖…

[Linux入门]---Linux编译器gcc/g++使用

文章目录 1.背景知识2.gcc如何完成编译运行工作预处理&#xff08;进行宏替换&#xff09;编译&#xff08;生成汇编&#xff09;汇编&#xff08;生成机器可识别代码&#xff09;链接&#xff08;生成可执行文件&#xff09; 3.函数库动态库静态库动静态库的区别 4.gcc选项 1.…

一键自助建站系统源码带安装教程 傻瓜式部署搭建,让您的建站更高效

在这个数字时代&#xff0c;网站已成为企业或个人展示形象、推广业务的重要工具。为了满足这一需求&#xff0c;许多自助建站系统应运而生&#xff0c;大大降低了用户建站的门槛。给大家分享一款傻瓜式部署搭建的一键自助建站系统源码&#xff0c;让您轻松拥有高效建站能力。 …

虹科教您 | 可实现带宽计量和延迟计算的时间敏感网络测试工具RELY-TSN-LAB操作指南与基本功能测试

1. RELY-TSN-LAB产品概述 时间敏感网络(TSN)能够合并OT和IT世界&#xff0c;这将是真正确保互操作性和标准化的创新性技术。这项技术的有效开发将显著降低设备成本、维护、先进分析服务的无缝集成以及减少对单个供应商的依赖。为了在这些网络中实现确定性&#xff0c;需要控制…

[LLM+AIGC] 01.应用篇之中文ChatGPT初探及利用ChatGPT润色论文对比浅析(文心一言 | 讯飞星火)

近年来&#xff0c;人工智能技术火热发展&#xff0c;尤其是OpenAI在2022年11月30日发布ChatGPT聊天机器人程序&#xff0c;其使用了Transformer神经网络架构&#xff08;GPT-3.5&#xff09;&#xff0c;能够基于在预训练阶段所见的模式、统计规律和知识来生成回答&#xff0c…

【Linux操作系统实战】Linux基础命令面试必备(二)

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想…

【rabbitMQ】-延迟队列-模拟控制智能家居的操作指令

这个需求为控制智能家居工作&#xff0c;把控制智能家居的操作指令发到队列中&#xff0c;比如&#xff1a;扫地机、洗衣机到指定时间工作 一.什么是延迟队列&#xff1f; 延迟队列存储的对象是对应的延迟消息&#xff0c;所谓“延迟消息” 是指当消息被发送以后&#xff0c;并…

数据中心中什么最重要?

在数据中心中&#xff0c;最重要的要素可以总结为以下几点&#xff1a; 数据安全&#xff1a;数据中心是存储和处理大量敏感数据的关键设施&#xff0c;因此数据安全是最重要的要素之一。数据中心必须采取严格的物理安全措施&#xff0c;如门禁、监控和防火措施&#xff0c;以确…

Stable Diffusion基础:精准控制之ControlNet

在AI绘画中精确控制图片的生成是一件比较困难的事情&#xff0c;炼丹师们经常需要大量抽卡才能得到一张满意的图片&#xff0c;不过随着 ControlNet 的诞生&#xff0c;这一问题得到了很大的缓解。 ControlNet 提供了十几种控制网络模型&#xff0c;有的可以控制画面的结构&…

利用大模型知识图谱技术,告别繁重文案,实现非结构化数据高效管理

我&#xff0c;作为一名产品经理&#xff0c;对文案工作可以说是又爱又恨&#xff0c;爱的是文档作为嘴替&#xff0c;可以事事展开揉碎讲清道明&#xff1b;恨的是只有一个脑子一双手&#xff0c;想一边澄清需求一边推广宣传一边发布版本一边申报认证实在是分身乏术&#xff0…

【uniapp】小程序开发:2 安装uni-ui组件库、使用pinia状态管理、自定义http请求

一、安装uni-ui组件库 1、安装 pnpm i -D sass pnpm i dcloudio/uni-ui2、配置组件自动导入 使用 npm 安装好 uni-ui 之后&#xff0c;需要配置 easycom 规则&#xff0c;让 npm 安装的组件支持 easycom 打开项目根目录下的 pages.json 并添加 easycom 节点&#xff1a; //…

LeetCode 75-03:拥有最多糖果的孩子

func kidsWithCandies(candies []int, extraCandies int) []bool {maxCandy : 0for _, v : range candies{if v > maxCandy{maxCandy v}}res : make([]bool, len(candies))for i,candy : range candies{res[i] candy extraCandies > maxCandy}return res }

对象的生命周期、配置文件参数化、自定义类型转换器

目录 一、对象的生命周期 1、什么是对象的生命周期 2、为什么要学习对象的生命周期 3、生命周期的三个阶段 &#xff08;1&#xff09;创建阶段 &#xff08;2&#xff09;初始化阶段 1、InitializingBean 接口 2、对象中提供一个普通的方法 3、细节分析 &#xff08…

linux离线安装make

一、下载rpm包 https://pkgs.org/search/?qmake 二、拷贝至服务器 三、安装make rpm -ivh make-3.82-24.el7.x86_64.rpm四、查看是否安装成功 make -v

Git 设置公钥

一、公钥管理 1、生成公钥 WinR&#xff0c;输入cmd&#xff0c;打开命令行窗口&#xff0c;执行ssh-keygen命令 查看生成的公钥&#xff0c;使用everything搜索id_rsa.pub&#xff0c;如下&#xff1a; 2、配置公钥 打开服务端网站&#xff0c;添加公钥 3、修改Git Tourtise配…

PostgreSQL 技术内幕(十)WAL log 模块基本原理

事务日志是数据库的重要组成部分&#xff0c;记录了数据库系统中所有更改和操作的历史信息。 WAL log(Write Ahead Logging)也被称为xlog&#xff0c;是事务日志的一种&#xff0c;也是关系数据库系统中用于保证数据一致性和事务完整性的一系列技术&#xff0c;在数据库恢复、高…

Ubuntu 安装golang

目录 1. 从官方网站下载合适的安装包 2. 解压安装包 3. 设置环境变量 4. 验证安装是否成功 1. 从官方网站下载合适的安装包 到官网&#xff1a;https://go.dev/dl/查找对应的版本 # 下载golang对应的版本 wget https://dl.google.com/go/go1.14.4.linux-amd64.tar.gz 2. …

PM2.5 激光粉尘传感器之DSL-08(DS-08)

一款产品里用到粉尘检测&#xff0c;最后在淘宝上找一到了一款&#xff0c;攀藤型号是&#xff1a;PMS9003 还有一款外形类似型号是DSL-08。看了资料是可以通用的。 传感器的产品资料放在百度网盘里 链接&#xff1a;https://pan.baidu.com/s/1oNCwO7n9oEdzju91cF99xg?pwd12…

PX4 通过 Vision 实现 Position、Altitude 和 Offboard 模式

本文通过 VINS-Fusion 的里程计信息为 PX4 提供视觉信息&#xff0c;从而达到 视觉定高和定点 的目的 主要工作为创建一个将 vins 里程计信息发布给 Mavros 的 /mavros/vision_pose/pose 话题 首先创建一个工作空间 mkdir -p ~/catkin_ws/src/vision_to_mavros/src/ cd ~/ca…