【1797. 设计一个验证系统】

news2024/11/28 6:38:17

来源:力扣(LeetCode)

描述:

  你需要设计一个包含验证码的验证系统。每一次验证中,用户会收到一个新的验证码,这个验证码在 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:
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 <= 108
  • 1 <= currentTime <= 108
  • 1 <= tokenId.length <= 5
  • tokenId 只包含小写英文字母。
  • 所有 generate 函数的调用都会包含独一无二的 tokenId 值。
  • 所有函数调用中,currentTime 的值 严格递增 。
  • 所有函数的调用次数总共不超过 2000 次。

方法一:哈希表

思路

  按照题意,用一个哈希表 map 保存验证码和过期时间。调用 generate 时,将验证码-过期时间对直接插入 map。调用 renew 时,如果验证码存在并且未过期,则更新过期时间。调用 countUnexpiredTokens 时,遍历整个 map,统计未过期的验证码的数量。

代码:

class AuthenticationManager {
private:
    int timeToLive;
    unordered_map<string, int> mp;
public:
    AuthenticationManager(int timeToLive) {
        this->timeToLive = timeToLive;
    }

    void generate(string tokenId, int currentTime) {
        mp[tokenId] = currentTime + timeToLive;
    }

    void renew(string tokenId, int currentTime) {
        if (mp.count(tokenId) && mp[tokenId] > currentTime) {
            mp[tokenId] = currentTime + timeToLive;
        }
    }

    int countUnexpiredTokens(int currentTime) {
        int res = 0;
        for (auto &[_, time] : mp) {
            if (time > currentTime) {
                res++;
            }
        }
        return res;
    }
};

执行用时:80 ms, 在所有 C++ 提交中击败了56.07%的用户
内存消耗:29.4 MB, 在所有 C++ 提交中击败了50.93%的用户
复杂度分析
时间复杂度:构造函数:O(1),generate:O(1),renew:O(1),countUnexpiredTokens:O(n),其中 n 为 generate 的调用次数。
空间复杂度:O(n),其中 n 为 generate 的调用次数,map 中有 n 个元素。

方法二:哈希表 + 双向链表

思路

用一个双向链表保存验证码和过期时间的顺序。链表的节点保存验证码和过期时间信息,并且在一条链表上,节点保存的过期时间是递增的。额外用一个哈希表 map 来保存验证码-节点对,提供根据验证码来快速访问节点的方法。调用 generate 时,新建节点,将节点插入链表末尾,并插入 map。调用 renew 时,如果验证码存在并且未过期,根据 map 访问到节点,更新过期时间,并将节点从原来位置移动到链表末尾。调用 countUnexpiredTokens 时,从链表头部开始,删除过期的节点,并从 map 删除。最后 map 的长度就是未过期的验证码的数量。

代码:

struct Node {   
public:
    int expire;
    string key;
    Node *prev;
    Node *next;

    Node(int expire_) : expire(expire_), prev(nullptr), next(nullptr) {
    }

    Node(int expire_, string key_) : expire(expire_), key(key_), prev(nullptr), next(nullptr) {
    }

    Node(int expire_, string key_, Node *prev_, Node *next_) : expire(expire_), key(key_), prev(prev_), next(next_) {
    }
};

class AuthenticationManager {
private:
    int timeToLive;
    Node *head;
    Node *tail;
    unordered_map<string, Node*> mp;
public:
    AuthenticationManager(int timeToLive) {
        this->timeToLive = timeToLive;
        this->head = new Node(-1);
        this->tail = new Node(-1);
        this->head->next = this->tail;
        this->tail->prev = this->head;
    }
    
    void generate(string tokenId, int currentTime) {
        Node *node = new Node(currentTime + timeToLive, tokenId);
        mp[tokenId] = node;
        Node *last = tail->prev;
        last->next = node;
        node->prev = last;
        tail->prev = node;
        node->next = tail;
    }
    
    void renew(string tokenId, int currentTime) {
        if (mp.count(tokenId) && mp[tokenId]->expire > currentTime) {
            Node *node = mp[tokenId];
            Node *prev = node->prev;
            Node *next = node->next;
            prev->next = next;
            next->prev = prev;
            node->expire = currentTime + timeToLive;
            Node *last = tail->prev;
            last->next = node;
            node->prev = last;
            tail->prev = node;
            node->next = tail;
        }
    }
    
    int countUnexpiredTokens(int currentTime) {
        while (head->next->expire > 0 && head->next->expire <= currentTime) {
            Node *node = head->next;
            mp.erase(node->key);
            head->next = node->next;
            node->next->prev = head;
            delete node;
        }
        return mp.size();
    }
};

执行用时:68 ms, 在所有 C++ 提交中击败了87.38%的用户
内存消耗:30.1 MB, 在所有 C++ 提交中击败了7.94%的用户
复杂度分析
时间复杂度:构造函数:O(1),generate:O(1),renew:O(1),所有 countUnexpiredTokens 的调用加起来的复杂度是 O(n),其中 n 为 generate 的总调用次数。
空间复杂度:O(n),其中 n 为 generate 的调用次数,map 和链表中有 n 个元素。
author:LeetCode-Solution

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

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

相关文章

游戏多开的分析与实现

大部分游戏为了防止工作室通过多开游戏牟利&#xff0c;都会采取各种手段来防止游戏被多次打开。检测窗口标题&#xff0c;创建互斥体&#xff0c;创建内存映射这些都是防止游戏多开的常用手段。 主要内容 游戏运行后&#xff0c;无非执行两步操作 若已经存在&#xff0c;退出…

【问题代码】顺序点的深入理解(汇编剖析+手画图解)

这好像是一个哲学问题。 目录 前言 一、顺序点是什么&#xff1f; 二、发生有关顺序点的问题代码 vs中&#xff1a; gcc中&#xff1a; 三、细读汇编 1.vs汇编如下&#xff08;示例&#xff09;&#xff1a; 2.gcc汇编如下&#xff08;示例&#xff09;&#xff1a; 四…

R语言raster包遍历多个文件夹并批量计算每一个文件夹下全部遥感影像的平均值

本文介绍基于R语言中的raster包&#xff0c;遍历读取多个文件夹下的多张栅格遥感影像&#xff0c;分别批量对每一个文件夹中的多个栅格图像计算平均值&#xff0c;并将所得各个结果栅格分别加以保存的方法。 其中&#xff0c;本文是用R语言来进行操作的&#xff1b;如果希望基于…

每天10个前端小知识 【Day 9】

前端面试基础知识题 1. bind、call、apply 有什么区别&#xff1f;如何实现一个bind? apply、call、bind三者的区别在于&#xff1a; 三者都可以改变函数的this对象指向三者第一个参数都是this要指向的对象&#xff0c;如果如果没有这个参数或参数为undefined或null&#x…

智能硬件的工作原理与发展定位

一、硬件概述 智能硬件是以平台性底层软硬件为基础&#xff0c;以智能传感互联、人机交互、新型显示及大数据处理等新一代信息技术为特征&#xff0c;以新设计、新材料、新工艺硬件为载体的新型智能终端产品及服务。 与传统硬件相比&#xff0c;智能硬件相比传统硬件&#xf…

【LeetCode每日一题】【2023/2/9】1797. 设计一个验证系统

文章目录1797. 设计一个验证系统方法1&#xff1a;哈希表代码总体1797. 设计一个验证系统 LeetCode: 1797. 设计一个验证系统 中等\color{#FFB800}{中等}中等 你需要设计一个包含验证码的验证系统。每一次验证中&#xff0c;用户会收到一个新的验证码&#xff0c;这个验证码在…

java 线程池

线程池概念 线程池可以看做是一个池子&#xff0c;在这个池子中存储了很多线程&#xff0c;线程池也可以说是一个复用线程的技术。 线程池存在的意义 系统创建一个线程的成本是比较高的&#xff0c;因为它涉及到与操作系统交互&#xff0c;当程序中需要创建大量生存期很短暂的线…

ChatGPT edge/chrome浏览器离线安装

最近chatgpt又热了起来&#xff0c;数据显示很多朋友过来下载浏览器插件&#xff0c;由于大家无法直接访问谷歌应用市场&#xff0c;因此提供一个离线安装的方式。 火热程度 对于大多资本与巨头来说&#xff0c;入局ChatGPT赛道&#xff0c;看中的无疑是ChatGPT概念背后的…

【自学Docker】Docker push命令

大纲 Docker push命令 docker push命令教程 docker push 命令用于将本地的 Docker镜像 上传到 Docker镜像仓库。 docker push命令使用之前需要要先登陆到镜像仓库。docker push命令推送镜像的规范是&#xff1a;注册用户名/镜像名。 docker push命令语法 haicoder(www.hai…

矩阵理论复习(十一)

正交投影矩阵的应用 值域与零空间 证明向量二范数 如何由已知范数构造新的范数 椭圆范数 向量范数的分析性质 向量范数的等价性 在无限维线性空间中&#xff0c;两个向量范数可以是不等价的。 等价性的重要意义&#xff1a;处理向量问题时&#xff0c;可以基于一种范…

ASO优化之如何进行榜单优化

ASO优化有&#xff1a;搜索优化&#xff0c;榜单优化&#xff0c;转化率优化。今天我们主要来讲讲苹果应用商店的榜单优化。 榜单优化的核心内容就是提高应用商城的排名&#xff0c;把我们的APP提升到显眼的位置&#xff0c;增加曝光率&#xff0c;提升APP的下载量。 那我们具…

easyx的基本使用(万字解析)

easyx的基本使用一.基本框架1.创建文件2.创建窗体-initgraph,closegraph,getchar二.简单的绘制1.圆形-circle2.坐标系统-setorigin,setaspectratio三.简单图形1.绘制点-putpixel2.简单的直线-line3.矩形-rectangle4.椭圆-ellipse5.圆角矩形-roundrect6.扇形-pie7.圆弧-arc四.多…

目标检测回归损失函数 IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU损失函数分析

目标检测回归损失函数 IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU损失函数分析 一、IOU Loss 2016文章《UnitBox: An Advanced Object Detection Network》中提出了IOU Loss将4个点构成的box看成一个整体做回归。 函数特性 IOU Loss的定义是先求出预测框和真实框…

UML的分类

一、UML2.0的正式图型 UML标准术语&#xff1a;UML标准术语_pingcode的博客-CSDN博客 英文名中文术语目的联系Class Diagram类图类、特征与关系UML1.x中有Component Diagram构件图构件的结构和连接UML1.x中有Composite Structure Diagram组合结构图类的运行时刻分解UML2.0的新…

前端——http的三次握手四次挥手

首先要知道在客户端与服务器端进行一个 http 请求的发送和返回的过程当中&#xff0c;我们需要创建一个 TCP 连接&#xff08;TCP connection&#xff09;&#xff1b;因为 http 只存在请求和响应&#xff0c;不存在连接这个概念的&#xff1b;请求和响应都是数据包&#xff0c…

牛客网 HJ56 完全数计算

前言&#xff1a;内容包括四大模块&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; 描述 完全数&#xff08;Perfect number&#xff09;&#xff0c;又称完美数或完备数&#xff0c;是一些特殊的自然数。 它所有的真因子…

sed和awk

文章目录1、sed的简单介绍2、sed的使用方法2.1 命令行格式2.2 案例2.3 sed结合正则使用2.4 脚本格式3、awk的简单介绍4、awk的使用方法4.1 命令行模式4.2 脚本模式5、awk内部相关变量5.1 案例6、awk工作原理7、awk进阶使用8、awk脚本编程8.1 案例1、sed的简单介绍 sed是流编辑…

【计算机网络】Linux环境中的网络套接字编程

文章目录前言一、预备知识理解源IP地址和目的IP地址认识端口号认识UDP协议和TCP协议了解网络字节序二、socket 套接字socket 常见APIsockaddr 和 sockaddr_in三、UDP Socket编程封装UdpSocket实现UDP通用服务器实现英译汉服务器实现UDP通用客户端实现英译汉客户端四、地址转换函…

一文详解Yolov5——基于Yolov5的火灾检测系统

✨原创不易&#xff0c;还希望各位大佬支持一下\textcolor{blue}{原创不易&#xff0c;还希望各位大佬支持一下}原创不易&#xff0c;还希望各位大佬支持一下 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01;\textcolor{green}{点赞&#xff0c;你的认可是…

SAP ERP系统实施隐式增强中“声明“和“代码“的区别和用途介绍

SAP ERP系统在实施隐式增强的时候会跳出一个增强模式选择“声明”或者“代码”,这步骤应该如何选择对于刚接触这类增强的开发人员通常会感到疑惑&#xff0c;不知道应该选择哪个&#xff08;如下图)。 点击“信息”可以看到官方的英文的解释如下&#xff1a; 这两个选项有什…