LeetCode 359: Logger Rate Limiter

link

For each unique message we keep the next valid timestamp. If a message is early, we simply discard it returning False. Otherwise, we update the next valid timestamp and return True.

Time: \mathcal{O}(1), space: \mathcal{O}(|\texttt{unique-messages}|).

class Logger:

    def __init__(self):
        self.next_message_time = {}

    def shouldPrintMessage(self, timestamp: int, message: str) -> bool:
        next_timestamp = self.next_message_time.get(message, -1)
        if timestamp < next_timestamp:
            return False

        self.next_message_time[message] = timestamp+10
        return True

# Your Logger object will be instantiated and called as such:
# obj = Logger()
# param_1 = obj.shouldPrintMessage(timestamp,message)

We can also do the filtering based on elapsed time.

Time: \mathcal{O}(1), space: \mathcal{O}(|\texttt{unique-messages}|).

class Logger:

    def __init__(self):
        self.message_time = {}

    def shouldPrintMessage(self, timestamp: int, message: str) -> bool:
        last_time = self.message_time.get(message, float("-inf"))
        elapsed_time = timestamp - last_time
        if elapsed_time < 10:
            return False
        
        self.message_time[message] = timestamp
        return True


# Your Logger object will be instantiated and called as such:
# obj = Logger()
# param_1 = obj.shouldPrintMessage(timestamp,message)

To optimize for space, we can periodically cleanup stale messages.

The cleanup trades off time for space. The worst-case for cleanup would be when messages are all new, because the dict will grow in size fast and it will be more prone to trigger a cleanup. However, we would only trigger a cleanup every 10 seconds. So, in this worst-case scenario, a message would be scanned by cleanup at most twice. As a result, the amortized cost of shouldPrintMessges remains \mathcal{O}(1).

class Logger:

    def __init__(self):
        self.last_cleanup_time = float("-inf")
        self.message_time = {}

    def __cleanup_stale_messages(self, timestamp):
        if len(self.message_time) < 1000 or (timestamp - self.last_cleanup_time) < 10:
            return

        for msg in self.message_time:
            if timestamp - message_time[msg] >= 10:
                del message_time[msg]

        self.last_cleanup_time = timestamp

    def shouldPrintMessage(self, timestamp: int, message: str) -> bool:
        self.__cleanup_stale_messages(timestamp)

        last_time = self.message_time.get(message, float("-inf"))
        if timestamp - last_time < 10:
            return False

        self.message_time[message] = timestamp
        return True


# Your Logger object will be instantiated and called as such:
# obj = Logger()
# param_1 = obj.shouldPrintMessage(timestamp,message)

Leave a comment