There is an authentication system that works with authentication tokens. For each session, the user will receive a new authentication token that will expire timeToLive
seconds after the currentTime
. If the token is renewed, the expiry time will be extended to expire timeToLive
seconds after the (potentially different) currentTime
.
Implement the AuthenticationManager
class:
AuthenticationManager(int timeToLive)
constructs the AuthenticationManager
and sets the timeToLive
.generate(string tokenId, int currentTime)
generates a new token with the given tokenId
at the given currentTime
in seconds.renew(string tokenId, int currentTime)
renews the unexpired token with the given tokenId
at the given currentTime
in seconds. If there are no unexpired tokens with the given tokenId
, the request is ignored, and nothing happens.countUnexpiredTokens(int currentTime)
returns the number of unexpired tokens at the given currentTime.Note that if a token expires at time t
, and another action happens on time t
(renew
or countUnexpiredTokens
), the expiration takes place before the other actions.
Example 1:
Input ["AuthenticationManager", "renew
", "generate", "countUnexpiredTokens
", "generate", "renew
", "renew
", "countUnexpiredTokens
"] [[5], ["aaa", 1], ["aaa", 2], [6], ["bbb", 7], ["aaa", 8], ["bbb", 10], [15]] Output [null, null, null, 1, null, null, null, 0] Explanation AuthenticationManager authenticationManager = new AuthenticationManager(5); // Constructs the AuthenticationManager withtimeToLive
= 5 seconds. authenticationManager.renew
("aaa", 1); // No token exists with tokenId "aaa" at time 1, so nothing happens. authenticationManager.generate("aaa", 2); // Generates a new token with tokenId "aaa" at time 2. authenticationManager.countUnexpiredTokens
(6); // The token with tokenId "aaa" is the only unexpired one at time 6, so return 1. authenticationManager.generate("bbb", 7); // Generates a new token with tokenId "bbb" at time 7. authenticationManager.renew
("aaa", 8); // The token with tokenId "aaa" expired at time 7, and 8 >= 7, so at time 8 therenew
request is ignored, and nothing happens. authenticationManager.renew
("bbb", 10); // The token with tokenId "bbb" is unexpired at time 10, so therenew
request is fulfilled and now the token will expire at time 15. authenticationManager.countUnexpiredTokens
(15); // The token with tokenId "bbb" expires at time 15, and the token with tokenId "aaa" expired at time 7, so currently no token is unexpired, so return 0.
Constraints:
1 <= timeToLive <= 108
1 <= currentTime <= 108
1 <= tokenId.length <= 5
tokenId
consists only of lowercase letters.generate
will contain unique values of tokenId
.currentTime
across all the function calls will be strictly increasing.2000
calls will be made to all functions combined.The problem requires designing an AuthenticationManager
class to manage authentication tokens. The manager has three core functionalities:
generate(tokenId, currentTime)
: Creates a new token with a given ID and sets its expiration time to currentTime + timeToLive
.renew(tokenId, currentTime)
: Extends the expiration time of an existing, unexpired token to currentTime + timeToLive
. Ignores the request if the token is expired or doesn't exist.countUnexpiredTokens(currentTime)
: Returns the number of tokens that haven't yet expired at the given time.The most efficient way to implement this is using a hash table (or dictionary in Python). The hash table will store token IDs as keys and their expiration times as values. This allows for O(1) average-case time complexity for generate
and renew
. countUnexpiredTokens
requires iterating through the hash table, resulting in O(n) time complexity where n is the number of tokens.
timeToLive
value.generate
: Add a new entry to the hash table with the tokenId
as the key and currentTime + timeToLive
as the value.renew
: Check if the tokenId
exists in the hash table and if its expiration time is greater than currentTime
. If both conditions are true, update the expiration time to currentTime + timeToLive
.countUnexpiredTokens
: Iterate through the hash table and count the number of entries where the expiration time is greater than currentTime
.from collections import defaultdict
class AuthenticationManager:
def __init__(self, timeToLive: int):
self.timeToLive = timeToLive
self.tokens = defaultdict(int) # Use defaultdict for easier handling of missing keys
def generate(self, tokenId: str, currentTime: int) -> None:
self.tokens[tokenId] = currentTime + self.timeToLive
def renew(self, tokenId: str, currentTime: int) -> None:
if tokenId in self.tokens and self.tokens[tokenId] > currentTime:
self.tokens[tokenId] = currentTime + self.timeToLive
def countUnexpiredTokens(self, currentTime: int) -> int:
count = 0
for expirationTime in self.tokens.values():
if expirationTime > currentTime:
count += 1
return count
Time Complexity:
generate
: O(1) on average (hash table insertion)renew
: O(1) on average (hash table lookup and update)countUnexpiredTokens
: O(n) in the worst case (iterating through all tokens)Space Complexity: O(n), where n is the number of active tokens stored in the hash table.
The same logic can be implemented in other languages using their equivalent hash table data structures:
HashMap
unordered_map
map[string]int
Map
HashMap
The code structure will be very similar across languages, mainly differing in syntax and data structure specifics. For example, here's a concise Java implementation:
import java.util.HashMap;
import java.util.Map;
class AuthenticationManager {
private int timeToLive;
private Map<String, Integer> tokens = new HashMap<>();
public AuthenticationManager(int timeToLive) {
this.timeToLive = timeToLive;
}
public void generate(String tokenId, int currentTime) {
tokens.put(tokenId, currentTime + timeToLive);
}
public void renew(String tokenId, int currentTime) {
if (tokens.containsKey(tokenId) && tokens.get(tokenId) > currentTime) {
tokens.put(tokenId, currentTime + timeToLive);
}
}
public int countUnexpiredTokens(int currentTime) {
int count = 0;
for (int expirationTime : tokens.values()) {
if (expirationTime > currentTime) {
count++;
}
}
return count;
}
}
The other languages would follow a very similar pattern. Remember to handle potential errors, such as missing keys, appropriately in each language's context.