Quick Overview

A coding problem about a cost-based rate limiter that processes ALLOW and RESET commands for many keys. Each key may spend a fixed budget per aligned time window and rejected requests record nothing, so it tests per-key window bookkeeping and 64-bit timestamp handling across up to 200,000 commands.

Per-Key Fixed-Window Cost Limiter with ALLOW and RESET Commands

Company: xAI

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

You are building a rate limiter that meters **cost** instead of counting requests. Every key (for example a user or an API client) may spend at most `limit` cost units in each fixed time window of `window` seconds. Windows are aligned to multiples of `window`: the window that contains timestamp `t` starts at `floor(t / window) * window` and ends just before `floor(t / window) * window + window`. Process a list of commands in order and return one output string per command. There are two kinds of command: - `ALLOW key cost timestamp`: let `usage` be the total cost already accepted for `key` in the window that contains `timestamp`. If `usage + cost <= limit`, accept the request, add `cost` to that usage, and output `"true"`. Otherwise output `"false"` and record nothing. - `RESET key timestamp`: clear all usage recorded for `key` and output `"ok"`. ### Function Signature ```python def process_commands(limit: int, window: int, commands: list[str]) -> list[str]: ``` ### Rules - Keys are independent: one key's usage never affects another key. - Usage accepted in one window never counts toward a later window. The first `ALLOW` for a key in a new window starts from zero usage. - A rejected `ALLOW` changes nothing, so a later, cheaper request in the same window can still be accepted. - A request whose `cost` alone is greater than `limit` is always rejected. - `RESET` may name a key that has never been seen; it still outputs `"ok"`. After a `RESET`, the key's usage in the current window is zero. - Outputs are the exact lowercase strings `"true"`, `"false"` and `"ok"`, one per command, in command order. ### Constraints - `1 <= len(commands) <= 200000` - `1 <= limit <= 10^9` and `1 <= window <= 10^9` - Every command is either `"ALLOW key cost timestamp"` or `"RESET key timestamp"`, with tokens separated by single spaces. `key` is a non-empty string of at most 20 lowercase letters and digits. `cost` and `timestamp` are decimal integers with no sign and no leading zeros (except the number `0` itself). - `1 <= cost <= 10^9` - `0 <= timestamp <= 10^15`. Timestamps and window starts can exceed `2^31 - 1`, so use 64-bit integers for them. - Timestamps are non-decreasing across the whole list: every command's timestamp is greater than or equal to the previous command's timestamp. - The returned list has exactly `len(commands)` elements and is uniquely determined by the input. ### Examples **Example 1** - Input: `limit = 10`, `window = 60`, `commands = ["ALLOW alice 3 0", "ALLOW alice 8 10", "ALLOW alice 7 61", "RESET alice 62", "ALLOW alice 10 63", "ALLOW alice 1 64"]` - Output: `["true", "false", "true", "ok", "true", "false"]` - Explanation: Timestamps `0` and `10` fall in the window starting at `0`. The cost `3` is accepted (usage 3), and then `3 + 8 = 11 > 10`, so the cost `8` is rejected. Timestamp `61` falls in the window starting at `60`, where usage starts at zero, so the cost `7` is accepted. `RESET` clears alice's usage, so the cost `10` is accepted (usage 10), and the final cost `1` is rejected because `10 + 1 > 10`. **Example 2** - Input: `limit = 5`, `window = 10`, `commands = ["ALLOW a 5 9", "ALLOW b 2 9", "ALLOW a 1 9", "ALLOW a 1 10", "ALLOW b 4 19", "ALLOW b 3 19", "ALLOW b 1 19", "RESET c 20", "ALLOW c 5 20"]` - Output: `["true", "true", "false", "true", "true", "false", "true", "ok", "true"]` - Explanation: At timestamp `9` (window starting at `0`), `a` spends its whole budget and its next request is rejected, while `b` is unaffected. Timestamp `10` starts a new window, so `a` is accepted again. At timestamp `19` (window starting at `10`), `b` starts from zero: the cost `4` is accepted, `4 + 3 > 5` is rejected and records nothing, so `4 + 1 = 5` is still accepted. `RESET` on the unseen key `c` outputs `"ok"`, and `c` can then spend its full budget. **Example 3** - Input: `limit = 999999999`, `window = 1000000000`, `commands = ["ALLOW k 1000000000 999999999999999", "ALLOW k 999999999 999999999999999", "ALLOW k 1 999999999999999", "ALLOW k 1 1000000000000000"]` - Output: `["false", "true", "false", "true"]` - Explanation: The first cost is larger than `limit`, so it is rejected. Timestamp `999999999999999` lies in the window starting at `999999000000000`, where `999999999` fits exactly and the extra `1` does not. Timestamp `1000000000000000` starts a new window at `1000000000000000`, so the cost `1` is accepted.

Overview: A coding problem about a cost-based rate limiter that processes ALLOW and RESET commands for many keys. Each key may spend a fixed budget per aligned time window and rejected requests record nothing, so it tests per-key window bookkeeping and 64-bit timestamp handling across up to 200,000 commands.

Build a rate limiter that meters **cost** rather than counting requests. Each key (for example a user or an API client) may spend at most `limit` cost units in each fixed time window of `window` seconds. Windows are aligned to multiples of `window`: the window that contains timestamp `t` starts at `floor(t / window) * window` and ends just before `floor(t / window) * window + window`. Process a list of commands in order and return one output string per command. There are two kinds of command: - `ALLOW key cost timestamp`: let `usage` be the total cost already accepted for `key` in the window that contains `timestamp`. If `usage + cost <= limit`, accept the request, add `cost` to that usage, and output `"true"`. Otherwise output `"false"` and record nothing. - `RESET key timestamp`: clear all usage recorded for `key` and output `"ok"`. Implement `process_commands(limit, window, commands)`, which returns the list of outputs. ### Rules - Keys are independent: one key's usage never affects another key. - Usage accepted in one window never counts toward a later window. The first `ALLOW` for a key in a new window starts from zero usage. - A rejected `ALLOW` changes nothing, so a later, cheaper request in the same window can still be accepted. - A request whose `cost` alone is greater than `limit` is always rejected. - `RESET` may name a key that has never been seen; it still outputs `"ok"`. After a `RESET`, the key's usage in the current window is zero. - Outputs are the exact lowercase strings `"true"`, `"false"` and `"ok"`, one per command, in command order. The returned list has exactly `len(commands)` elements and is uniquely determined by the input. ### Constraints - `1 <= len(commands) <= 200000` - `1 <= limit <= 10^9` and `1 <= window <= 10^9` - Every command is either `"ALLOW key cost timestamp"` or `"RESET key timestamp"`, with tokens separated by single spaces. `key` is a non-empty string of at most 20 lowercase letters and digits. `cost` and `timestamp` are decimal integers with no sign and no leading zeros (except the number `0` itself). - `1 <= cost <= 10^9` - `0 <= timestamp <= 10^15`. Timestamps and window starts can exceed `2^31 - 1`, so use 64-bit integers for them. - Timestamps are non-decreasing across the whole list: every command's timestamp is greater than or equal to the previous command's timestamp. ### Example 1 ``` Input: limit = 10, window = 60, commands = ["ALLOW alice 3 0", "ALLOW alice 8 10", "ALLOW alice 7 61", "RESET alice 62", "ALLOW alice 10 63", "ALLOW alice 1 64"] Output: ["true", "false", "true", "ok", "true", "false"] ``` Timestamps `0` and `10` fall in the window starting at `0`. The cost `3` is accepted (usage 3), and then `3 + 8 = 11 > 10`, so the cost `8` is rejected. Timestamp `61` falls in the window starting at `60`, where usage starts at zero, so the cost `7` is accepted. `RESET` clears alice's usage, so the cost `10` is accepted (usage 10), and the final cost `1` is rejected because `10 + 1 > 10`. ### Example 2 ``` Input: limit = 5, window = 10, commands = ["ALLOW a 5 9", "ALLOW b 2 9", "ALLOW a 1 9", "ALLOW a 1 10", "ALLOW b 4 19", "ALLOW b 3 19", "ALLOW b 1 19", "RESET c 20", "ALLOW c 5 20"] Output: ["true", "true", "false", "true", "true", "false", "true", "ok", "true"] ``` At timestamp `9` (window starting at `0`), `a` spends its whole budget and its next request is rejected, while `b` is unaffected. Timestamp `10` starts a new window, so `a` is accepted again. At timestamp `19` (window starting at `10`), `b` starts from zero: the cost `4` is accepted, `4 + 3 > 5` is rejected and records nothing, so `4 + 1 = 5` is still accepted. `RESET` on the unseen key `c` outputs `"ok"`, and `c` can then spend its full budget. ### Example 3 ``` Input: limit = 999999999, window = 1000000000, commands = ["ALLOW k 1000000000 999999999999999", "ALLOW k 999999999 999999999999999", "ALLOW k 1 999999999999999", "ALLOW k 1 1000000000000000"] Output: ["false", "true", "false", "true"] ``` The first cost is larger than `limit`, so it is rejected. Timestamp `999999999999999` lies in the window starting at `999999000000000`, where `999999999` fits exactly and the extra `1` does not. Timestamp `1000000000000000` starts a new window at `1000000000000000`, so the cost `1` is accepted.

Constraints

  • 1 <= len(commands) <= 200000
  • 1 <= limit <= 10^9
  • 1 <= window <= 10^9
  • Every command is either "ALLOW key cost timestamp" or "RESET key timestamp", with tokens separated by single spaces
  • key is a non-empty string of at most 20 lowercase letters and digits
  • cost and timestamp are decimal integers with no sign and no leading zeros (except the number 0 itself)
  • 1 <= cost <= 10^9
  • 0 <= timestamp <= 10^15; timestamps and window starts can exceed 2^31 - 1, so use 64-bit integers for them
  • Timestamps are non-decreasing across the whole list

Examples

Input: (10, 60, ['ALLOW alice 3 0', 'ALLOW alice 8 10', 'ALLOW alice 7 61', 'RESET alice 62', 'ALLOW alice 10 63', 'ALLOW alice 1 64'])

Expected Output: ['true', 'false', 'true', 'ok', 'true', 'false']

Explanation: Source Example 1: over-budget request rejected, new window starts from zero, RESET clears usage.

Input: (5, 10, ['ALLOW a 5 9', 'ALLOW b 2 9', 'ALLOW a 1 9', 'ALLOW a 1 10', 'ALLOW b 4 19', 'ALLOW b 3 19', 'ALLOW b 1 19', 'RESET c 20', 'ALLOW c 5 20'])

Expected Output: ['true', 'true', 'false', 'true', 'true', 'false', 'true', 'ok', 'true']

Explanation: Source Example 2: independent keys, boundary at 10, rejected request records nothing, RESET on an unseen key.

Hints

  1. Only the current window matters for each key. You never need usage from an earlier window, so you do not need a history of requests.
  2. Store two numbers per key: the start of the window its usage belongs to, and that usage. When a request maps to a different window start, treat the stored usage as zero.
  3. Check the integer widths before you code: timestamps and window starts go up to 10^15, far beyond 32-bit range.

Community answers

Answer by shail.finaspirant

def process_commands(limit: int, window: int, commands: list[str]) -> list[str]: users = {} ans = [] for parts in commands: op = parts.split() cmd, user = op[0], op[1] if cmd == 'ALLOW': cost, ts = int(op[2]),int(op[3]) if cost > limit: ans.append("false") continue win_end = (ts//window) * window + window if user not in users or users[user][0] != win_end: users[user] = (win_end,cost) ans.append("true") elif users[user][1] + cost <= limit: users[user] = (win_end,users[user][1] + cost) ans.append("true") else: ans.append("false") elif cmd == 'RESET': users.pop(user, None) ans.append("ok") return ans

Loading coding console...

Show the approach

Approach

Only one window matters for any key at any moment: the window containing the request's timestamp. Timestamps never decrease, so once a key's requests move into a later window, the usage from the earlier window can never count again. That means you need no request history, only a small record per key.

The reference keeps a hash map from each key to a pair (window_start, usage), meaning "this key has accepted usage cost units in the window that starts at window_start".

For ALLOW key cost timestamp, compute start = floor(timestamp / window) * window. If the stored pair exists and its window_start equals start, the current usage is the stored usage. Otherwise (a new key, or usage left over from an older window) the current usage is 0. If usage + cost <= limit, store (start, usage + cost) and output "true". Otherwise output "false" and leave the map untouched. Leaving it untouched is safe even when the stored pair belongs to an older window, because the next ALLOW repeats the same window comparison.

For RESET key timestamp, delete the key from the map and output "ok". A missing entry already means zero usage, so deleting a key that was never seen is harmless.

Integer widths matter in typed languages. Timestamps and window starts reach 10^15, far beyond 32-bit range, so they must be 64-bit integers (long in Java, long long in C++); parsing a timestamp into a 32-bit int either throws or wraps and puts the request in the wrong window. usage + cost is at most 2 * 10^9, which still fits a signed 32-bit int, but the reference keeps it 64-bit too so that no mixed-width arithmetic is needed. In JavaScript every value stays below 2^53, so plain numbers are exact, and timestamp - timestamp % window gives the window start without floating-point division.

Each command costs one split of a short string and one hash-map operation, so the whole run is linear in the number of commands.

Time complexity:
O(n) for n = len(commands): each command has at most about 50 characters, so parsing it and one hash-map lookup take constant time
Space complexity:
O(k) for the per-key state, where k <= n is the number of distinct keys, plus O(n) for the output list