【1797. 设计一个验证系统】
创始人
2024-05-24 07:02:51
0

来源:力扣(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 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 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

上一篇:Qt 5 架构和特点

下一篇:Qt学习笔记

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...