【LeetCode每日一题】【2023/2/9】1797. 设计一个验证系统
文章目录
- 1797. 设计一个验证系统
- 方法1:哈希表
- 代码总体
1797. 设计一个验证系统
LeetCode: 1797. 设计一个验证系统
中等\color{#FFB800}{中等}中等
你需要设计一个包含验证码的验证系统。每一次验证中,用户会收到一个新的验证码,这个验证码在
currentTime时刻之后timeToLive秒过期。如果验证码被更新了,那么它会在currentTime(可能与之前的currentTime不同)时刻延长timeToLive秒。请你实现
AuthenticationManager类:
AuthenticationManager(int timeToLive)构造AuthenticationManager并设置timeToLive参数。generate(string tokenId, int currentTime)给定tokenId,在当前时间currentTime生成一个新的验证码。renew(string tokenId, int currentTime)将给定tokenId且 未过期 的验证码在currentTime时刻更新。如果给定tokenId对应的验证码不存在或已过期,请你忽略该操作,不会有任何更新操作发生。countUnexpiredTokens(int currentTime)请返回在给定currentTime时刻,未过期 的验证码数目。如果一个验证码在时刻
t过期,且另一个操作恰好在时刻t发生(renew或者countUnexpiredTokens操作),过期事件 优先于 其他操作。
示例 1:

输入:
["AuthenticationManager", "renew", "generate", "countUnexpiredTokens", "generate", "renew", "renew", "countUnexpiredTokens"]
[[5], ["aaa", 1], ["aaa", 2], [6], ["bbb", 7], ["aaa", 8], ["bbb", 10], [15]]
输出:
[null, null, null, 1, null, null, null, 0]解释:
AuthenticationManager authenticationManager = new AuthenticationManager(5); // 构造 AuthenticationManager ,设置 timeToLive = 5 秒。
authenticationManager.renew("aaa", 1); // 时刻 1 时,没有验证码的 tokenId 为 "aaa" ,没有验证码被更新。
authenticationManager.generate("aaa", 2); // 时刻 2 时,生成一个 tokenId 为 "aaa" 的新验证码。
authenticationManager.countUnexpiredTokens(6); // 时刻 6 时,只有 tokenId 为 "aaa" 的验证码未过期,所以返回 1 。
authenticationManager.generate("bbb", 7); // 时刻 7 时,生成一个 tokenId 为 "bbb" 的新验证码。
authenticationManager.renew("aaa", 8); // tokenId 为 "aaa" 的验证码在时刻 7 过期,且 8 >= 7 ,所以时刻 8 的renew 操作被忽略,没有验证码被更新。
authenticationManager.renew("bbb", 10); // tokenId 为 "bbb" 的验证码在时刻 10 没有过期,所以 renew 操作会执行,该 token 将在时刻 15 过期。
authenticationManager.countUnexpiredTokens(15); // tokenId 为 "bbb" 的验证码在时刻 15 过期,tokenId 为 "aaa" 的验证码在时刻 7 过期,所有验证码均已过期,所以返回 0 。
提示:
1 <= timeToLive <= 10^81 <= currentTime <= 10^81 <= tokenId.length <= 5tokenId只包含小写英文字母。- 所有
generate函数的调用都会包含独一无二的tokenId值。 - 所有函数调用中,
currentTime的值 严格递增 。 - 所有函数的调用次数总共不超过
2000次。
方法1:哈希表
使用哈希表来存放验证码及其过期时间。哈希表的键为验证码字符串,值为对应的过期时间。
构造函数将 timeToLive 记录下来:
class AuthenticationManager
{
public:explicit AuthenticationManager(const int timeToLive): ttl{timeToLive} { }private:const int ttl;
};
函数 generate() 将传入的 tokenId 记录下来,并将 currentTime 加上 ttl 作为该验证码的 过期时间 记录下来:
void generate(string tokenId, const int currentTime)
{hashtable.emplace(std::move(tokenId), currentTime + ttl);
}
函数 renew() 实现验证码的更新。按题意,若哈希表中没有找到该验证码,则不做任何操作;若该验证码的过期时间 小于等于 当前时间 currentTime ,则删除,否则更新为 currentTime + ttl ,即在当前时间下 延后 ttl 个时间。
void renew(const string& tokenId, const int currentTime)
{if (const auto it = hashtable.find(tokenId);it != hashtable.end()){if (it->second > currentTime)it->second = currentTime + ttl;elsehashtable.erase(it);}
}
函数 countUnexpiredTokens 在调用函数 clearExpiredTokens 清除 过期 验证码后,返回哈希表中的元素个数,即为所求的当前 未过期 的验证码。
int countUnexpiredTokens(const int currentTime)
{clearExpiredTokens(currentTime);return static_cast<int>(hashtable.size());
}
函数 clearExpiredTokens 遍历哈希表,并逐个删除 过期的 验证码:
void clearExpiredTokens(const int currentTime)
{for (auto it = hashtable.begin(); it != hashtable.end();){if (const auto& [_, expireTime] = *it;expireTime <= currentTime)it = hashtable.erase(it);else++it;}
}
代码总体
#include <string>
#include <unordered_map>
using namespace std;class AuthenticationManager
{
public:explicit AuthenticationManager(const int timeToLive): ttl{timeToLive} { }void generate(string tokenId, const int currentTime){hashtable.emplace(std::move(tokenId), currentTime + ttl);}void renew(const string& tokenId, const int currentTime){if (const auto it = hashtable.find(tokenId);it != hashtable.end()){if (it->second > currentTime)it->second = currentTime + ttl;elsehashtable.erase(it);}}int countUnexpiredTokens(const int currentTime){clearExpiredTokens(currentTime);return static_cast<int>(hashtable.size());}private:void clearExpiredTokens(const int currentTime){for (auto it = hashtable.begin(); it != hashtable.end();){if (const auto& [_, expireTime] = *it;expireTime <= currentTime)it = hashtable.erase(it);else++it;}}private:const int ttl;unordered_map<string, int> hashtable;
};
复杂度分析:
-
时间复杂度:
- 构造函数:O(1)O(1)O(1)
- generate():O(1)O(1)O(1),哈希表的插入操作即为 O(1)O(1)O(1) 的时间复杂度。
- renew():O(1)O(1)O(1),哈希表的查找、删除操作均为 O(1)O(1)O(1) 的时间复杂度。
- countUnexpiredTokens():O(n)O(n)O(n),其中 nnn 为哈希表中的验证码元素个数,或者说就是函数
generate()的调用次数。
-
空间复杂度:O(n)O(n)O(n)。其中 nnn 为函数
generate()的调用次数,主要为哈希表的开销。
参考结果
Accepted
90/90 cases passed (76 ms)
Your runtime beats 65.89 % of cpp submissions
Your memory usage beats 89.25 % of cpp submissions (29.3 MB)
若每次调用
generate()和renew()时都打算清理 过期的 验证码,那么由于函数clearExpiredTokens()的时间复杂度为 O(n)O(n)O(n) ,函数generate()和renew()的时间复杂度也会继而变为 O(n)O(n)O(n) 。参考结果如下:
Accepted 90/90 cases passed (92 ms) Your runtime beats 34.58 % of cpp submissions Your memory usage beats 93.46 % of cpp submissions (29.3 MB)
相关文章:
【LeetCode每日一题】【2023/2/9】1797. 设计一个验证系统
文章目录1797. 设计一个验证系统方法1:哈希表代码总体1797. 设计一个验证系统 LeetCode: 1797. 设计一个验证系统 中等\color{#FFB800}{中等}中等 你需要设计一个包含验证码的验证系统。每一次验证中,用户会收到一个新的验证码,这个验证码在…...
计算机图形学:改进的中点BH算法
作者:非妃是公主 专栏:《计算机图形学》 博客地址:https://blog.csdn.net/myf_666 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 文章目录专栏推荐专栏系列文章序一、改进缘由二、…...
【SQL开发实战技巧】系列(六):从执行计划看NOT IN、NOT EXISTS 和 LEFT JOIN效率,记住内外关联条件不要乱放
系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些事 【SQL开发实战技巧】系列(二):简单单表查询 【SQL开发实战技巧】系列(三):SQL排序的那些事 【SQL开发实战技巧…...
十分钟利用环信WebIM-vue3-Demo,打包上线一个即时通讯项目【含音视频通话】
这篇文章无废话,只教你如果接到即时通讯功能需求,十分钟利用环信WebIM-vue3-Demo,打包上线一个即时通讯项目【包含音视频通话功能】。 写这篇文章是因为,结合自身情况,以及所遇到的有同样情况的开发者在接到即时通讯&a…...
pandas——DataFrame基本操作(二)【建议收藏】
pandas——DataFrame基本操作(二) 文章目录pandas——DataFrame基本操作(二)一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.修改数据2.缺失值3.合并1.concat合并2.使用append方法合并3.使用merge进行合并4.使用…...
PostgreSQL查询引擎——General Expressions Grammar之restricted expression
General expressions语法规则定义在src/backend/parser/gram.y文件中,其是表达式语法的核心。有两种表达式类型:a_expr是不受限制的类型,b_expr是必须在某些地方使用的子集,以避免移位/减少冲突。例如,我们不能将BETWE…...
从某种程度上来看,产业互联网是一次对于互联网的弥补和修正
如果对当下我们正在经历的这样一个时代进行一次定义的话,我更加愿意将其划归到产业互联网的范畴里。可能有人会说,这与产业互联网并无联系,因为从本质上来看,当下我们所经历的这样一个时代,其实是与互联网并没有太多联…...
【C#Unity题】1.委托和事件在使用上的区别是什么?2.C#中 == 和 Equals 的区别是什么?
1.委托和事件在使用上的区别是什么? 委托和事件是C#中的重要概念,通俗来讲,委托是一个可以指向特定方法的指针,可以将委托分配给不同的脚本,使它们能够完成不同的任务。而事件则是一种使用委托实现的通知机制ÿ…...
FFmpeg5.0源码阅读——内存池AVBufferPool
摘要:FFmpeg中大多数数据存储比如AVFrame,AVPacket都是通过AVBufferRef管理的,而承载数据的结构为AVBuffer。本文主要通过FFmpeg源码来分析下FFmpeg中AVBuffer相关的实现。 关键字:AVBuffer、AVBufferPool、AVBufferPool 1. AVBufferRef 1.…...
Python学习------起步7(字符串的连接、删除、修改、查询与统计、类型判断及字符串字母大小写转换)
目录 前言: 1.字符串的连接 join() 函数 2.字符串的删除&取代 replace()函数 3.字符串的修改&切割 (1)strip() 函数 (2)lstrip()函数 和 rstrip()函数 (3)split()函数-->…...
雪花算法snowflake
snowflake中文的意思是 雪花,雪片,所以翻译成雪花算法。它最早是twitter内部使用的分布式环境下的唯一ID生成算法。在2014年开源。雪花算法产生的背景当然是twitter高并发环境下对唯一ID生成的需求,得益于twitter内部高超的技术,雪…...
Part 4 描述性统计分析(占比 10%)——上
文章目录【后续会持续更新CDA Level I&II备考相关内容,敬请期待】【考试大纲】【考试内容】【备考资料】1、统计基本概念1.1、统计学的含义及应用1.1.1、统计学的含义1.2.1、统计学的应用1.2、统计学的基本概念1.2.1、数据及数据的分类1.2.2、总体和样本1.2.3、…...
Linux系统安全:安全技术和防火墙
目录 一、安全技术 1、安全技术 2、防火墙分类 二、防火墙 1、iptables五表五链 2、黑白名单 3、iptables基本语法 4、iptables选项 5、控制类型 6、隐藏扩展模块 7、显示扩展模块 8、iptables规则保存 9、自定义链使用 一、安全技术 1、安全技术 ①入侵检测系统…...
【干货】Python:turtle库的用法
【干货】Python:turtle库的用法1. turtle库概述2. turtle库与基本绘图2.1 导入库的三种方式2.1.12.1.22.1.32.2 窗体函数2.2 画笔状态函数2.2.1 seed(s)2.2.2 random()2.2.3 randint(a, b)2.2.4 getrandbits(k)2.2.5 randrange(start, stop[ , step])2.2.6 uniform(…...
信息安全与网络安全有什么区别?
生活中我们经常会听到要保障自己的或者企业的信息安全。那到底什么是信息安全呢?信息安全包含哪些内容?与网络安全又有什么区别呢?今天我们就一起来详细了解一下。什么叫做信息安全?信息安全定义如下:为数据处理系统建…...
花了5年时间,用过市面上95%的工具,终于找到这款万能报表工具
经常有粉丝问我有“哪个报表工具好用易上手?”或者是“有哪些适合绝大多数普通职场人的万能报表工具?” 从这里我大概总结出了大家选择报表工具最期望满足的3点: (1)简单易上手:也就是所谓的学习门槛要低…...
ESP32S3系列--SPI主机驱动详解(一)
一、目的SPI是一种串行同步接口,可用于与外围设备进行通信。ESP32S3自带4个SPI控制器外设,其中SPI0/SPI1内部专用,共用一组信号线,通过一个仲裁器访问外部Flash和PSRAM;SPI2/3各自使用一组信号线;开发者可以使用SPI2/3控制外部SPI…...
2023开工开学火热!远行的人们,把淘特箱包送上顶流
春暖花开,被疫情偷走的三年在今年开学季找补回来了。多个数据反馈,居民消费意愿大幅提升。在淘特上,开工开学节点就很是明显:1月30日以来,淘特箱包品类甚至远超2022年双11,成为开年“第一爆品”。与此同时&…...
Intel x86_64 PMU简介
文章目录前言一、性能监控概述二、CPUID information三、架构性能监控3.1 架构性能监控 Version 13.1.1 架构性能监控 Version 1 Facilities3.1.2 预定义的体系结构性能事件3.1.3 cmask demo测试参考资料前言 Intel 64 和 IA-32 架构提供了 PMU(Performance Monito…...
Vue (2)
文章目录1. 模板语法1.1 插值语法1.2 指令语法2. 数据绑定3. 穿插 el 和 data 的两种写法4. MVVM 模型1. 模板语法 root 容器中的代码称为 vue 模板 1.1 插值语法 1.2 指令语法 图一 : 简写 : v-bind: 是可以简写成 : 的 总结 : …...
面向对象编程入门(下篇):继承、封装与多态
在上篇中,我们学会了如何定义类和创建对象,将现实世界的事物用代码表示。今天,我们将深入面向对象编程的三大核心特性:继承、封装和多态。这些特性将让你的代码更加灵活、可扩展和易维护。一、继承:代码复用的“家族传…...
Windows系统卡顿?一招禁用Microsoft Compatibility Telemetry释放CPU资源(附详细截图)
Windows系统卡顿终极解决方案:彻底禁用Microsoft Compatibility Telemetry 最近帮朋友处理一台老笔记本时,遇到了典型的Windows系统卡顿问题——风扇狂转、程序响应迟缓,任务管理器里一个叫"Microsoft Compatibility Telemetry"的进…...
Python 3.15 JIT为何在Docker中静默禁用?揭开musl libc与libffi-3.4.6 ABI不兼容的致命链
第一章:Python 3.15 JIT 的设计目标与 Docker 场景适配性Python 3.15 引入的实验性 JIT(Just-In-Time)编译器并非追求通用性能提升,而是聚焦于特定高价值场景——尤其是容器化微服务中反复执行的 CPU 密集型工作负载。其核心设计目…...
农机经销商必看:如何用2000-2020年县级数据精准定位区域市场?
农机经销商区域市场精准定位实战指南:基于2000-2020年县级数据分析 站在山东潍坊的田间地头,老张望着远处几台正在作业的拖拉机陷入了沉思。作为一家中型农机经销商的区域经理,他每年最头疼的就是如何准确预测各县区的农机需求——备货多了占…...
Python多进程+ZeroMQ+内存映射=真无锁?资深架构师用17个生产事故告诉你为什么92%的“去GIL”方案在高并发下静默失败
第一章:Python无锁GIL环境下的并发模型避坑指南Python 的全局解释器锁(GIL)长期被误认为是“无锁”环境,实则恰恰相反——GIL 是 CPython 解释器中一把严格的互斥锁,它确保任意时刻仅有一个线程执行 Python 字节码。所…...
别再只盯着GPS了!从手机导航到无人机测绘,聊聊SPP、DGPS、RTK、PPP这几种定位技术到底该怎么选?
定位技术实战指南:从厘米级精度到全球覆盖的智能决策 站在一片待测绘的工地上,无人机工程师小王正面临一个关键抉择——该为这批新设备配置哪种定位模块?RTK的厘米级精度令人心动,但架设基准站的成本让他犹豫;PPP技术号…...
手把手教你用RTABMAP+T265在Windows10上实现室内三维扫描(含标定技巧)
手把手教你用RTABMAPT265在Windows10上实现高精度室内三维扫描 第一次接触室内三维扫描时,我被这项技术深深吸引——它能让物理空间瞬间数字化,就像给现实世界按下"CtrlC"。但真正动手配置RTABMAP和T265相机时,才发现这条路并不平坦…...
如何快速搭建QQ机器人?LuckyLilliaBot入门指南
如何快速搭建QQ机器人?LuckyLilliaBot入门指南 【免费下载链接】LuckyLilliaBot NTQQ的OneBot API插件 项目地址: https://gitcode.com/gh_mirrors/li/LuckyLilliaBot 在数字化时代,QQ机器人开发已成为自动化交互的重要工具。LuckyLilliaBot作为N…...
rBase64:嵌入式系统零堆分配BASE64编解码库
1. rBase64 库深度解析:面向嵌入式系统的高性能 BASE64 编解码实现BASE64 是一种将任意二进制数据映射为 ASCII 字符子集的编码方案,广泛应用于嵌入式通信协议(如 MQTT payload、HTTP Basic Auth、CoAP 传输)、固件 OTA 升级包签名…...
沈阳装修靠谱的机构
在沈阳装修新家,最怕遇到不靠谱的装修公司——工期拖延、增项不断、工艺粗糙、售后无门。想要省心、放心、安心地完成装修,选择一家经验丰富、工艺扎实、信誉良好的机构至关重要。在众多沈阳装修公司中,沈阳富田装饰装修工程有限公司以其深厚…...
