diff --git a/data_structures/queues/design_hit_counter.py b/data_structures/queues/design_hit_counter.py new file mode 100644 index 000000000000..b72dc11176fa --- /dev/null +++ b/data_structures/queues/design_hit_counter.py @@ -0,0 +1,39 @@ +from collections import deque + + +class HitCounter: + """ + A counter that records hits (events) and can return + the number of hits in the past 5 minutes (300 seconds). + + >>> hc = HitCounter() + >>> hc.hit(1) + >>> hc.hit(2) + >>> hc.hit(300) + >>> hc.get_hits(300) + 3 + >>> hc.get_hits(301) + 2 + """ + + def __init__(self) -> None: + self.hits: deque[int] = deque() + + def hit(self, timestamp: int) -> None: + """Record a hit at the given timestamp (in seconds).""" + self.hits.append(timestamp) + + def get_hits(self, timestamp: int) -> int: + """ + Return the number of hits in the past 5 minutes + from the given timestamp. + """ + while self.hits and self.hits[0] <= timestamp - 300: + self.hits.popleft() + return len(self.hits) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/dynamic_programming/triangle.py b/dynamic_programming/triangle.py new file mode 100644 index 000000000000..9fdfc1f8d9cd --- /dev/null +++ b/dynamic_programming/triangle.py @@ -0,0 +1,42 @@ +from __future__ import annotations + + +def minimum_total(triangle: list[list[int]]) -> int: + """ + Return the minimum path sum from top to bottom of a triangle. + + Each step you may move to adjacent numbers on the row below. + The input must be a proper triangle: len(row i) == i + 1. + + >>> minimum_total([[2],[3,4],[6,5,7],[4,1,8,3]]) + 11 + >>> minimum_total([[-10]]) + -10 + >>> minimum_total([[1],[2,3]]) + 3 + >>> minimum_total([]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ValueError: triangle must be non-empty and well-formed + >>> minimum_total([[1],[2]]) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ValueError: triangle must be non-empty and well-formed + """ + # Basic validation: non-empty and proper "triangle" shape + if not triangle or any(len(row) != i + 1 for i, row in enumerate(triangle)): + raise ValueError("triangle must be non-empty and well-formed") + + # Start from the last row and fold upwards. dp[j] stores the minimum path + # sum from row r to the bottom starting at column j. + dp = triangle[-1][:] # copy so we don't mutate the input + + for r in range(len(triangle) - 2, -1, -1): + for c in range(r + 1): + dp[c] = triangle[r][c] + min(dp[c], dp[c + 1]) + + return dp[0] + + +if __name__ == "__main__": + import doctest + + doctest.testmod()