Quick Overview

A coding question about a brokerage that fills fractional-share orders from a small inventory while the exchange trades only whole shares. Given buy and sell orders in hundredths of a share or in dollars, plus the starting inventory, compute each stock's final inventory, which must stay non-negative and below one share after every order.

Fractional Share Orders: Keep Brokerage Inventory Below One Share

Company: Robinhood

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

A brokerage lets its customers buy and sell fractional shares, but the exchange trades only whole shares. To bridge the gap, the brokerage keeps a small inventory of each stock, fills fractional orders from that inventory, and trades whole shares on the exchange only when it has to. For example, suppose the brokerage holds no shares of a stock. A customer buys 1.5 shares, so the brokerage buys 2 whole shares on the exchange, delivers 1.5 and keeps 0.5. A second customer buys 0.4 shares, which come out of inventory and leave 0.1. A third customer buys 0.5 shares; inventory covers only 0.1 of them, so the brokerage buys 1 more whole share and is left with 0.6. Sales work in the other direction: if one customer sells 0.75 shares and another sells 0.5, inventory reaches 1.25, so the brokerage sells 1 whole share on the exchange and keeps 0.25. After every order, the brokerage flattens its inventory of that stock: the inventory is never negative, and it is always less than one share. Given the orders, in the order they arrive, and the brokerage's starting inventory, return its inventory of every stock after all orders have been handled. ### Function Signature ```python def final_inventory(orders: list[list[str]], inventory: list[list[str]]) -> list[list[str]]: ``` Each order is `[symbol, side, quantity, price]` and each inventory row is `[symbol, amount]`. Every number is an integer string in hundredths: its last two digits are the decimal part, so `"1000"` means 10.00, `"20"` means 0.20 and `"100"` means 1.00. ### Rules - `side` is `"B"` when the customer buys from the brokerage and `"S"` when the customer sells to it. - `price` is the current price of one share in hundredths of a dollar (cents). It is used only to convert dollar-based orders. - A `quantity` made only of digits is a number of shares in hundredths: `"42"` is 0.42 shares. - A `quantity` that starts with the character `$` is a dollar-based order: the digits after that character are a dollar amount in cents, and the order is for `amount * 100 / price` hundredths of a share (see Example 2). - Orders are handled one at a time, in list order, and each order changes only the inventory of its own `symbol`. A symbol that is not in `inventory` starts at 0. - **Buy of `q` hundredths.** If the inventory is at least `q`, the customer's shares come out of the inventory. Otherwise the brokerage first buys on the exchange the smallest number of whole shares that makes the inventory at least `q`, then delivers `q` from it. - **Sell of `q` hundredths.** `q` is added to the inventory. The brokerage then sells on the exchange as many whole shares as it can without making the inventory negative. - These rules leave every symbol's inventory between 0 and 99 hundredths, inclusive, after every order. - Return one `[symbol, amount]` row for every symbol that appears in `inventory` or in at least one order, including symbols whose final inventory is 0, sorted by `symbol` in ascending order. `amount` is the final inventory in hundredths of a share, written as a base-10 integer without leading zeros: `"8"`, not `"08"`, and `"0"` for an empty inventory. Return an empty list when both inputs are empty. ### Constraints - `0 <= len(orders) <= 10^5` - `0 <= len(inventory) <= 10^4` - Every `symbol` is 1 to 5 uppercase English letters, and a symbol appears at most once in `inventory`. Symbols are compared as plain strings, so `"AA"` sorts before `"AAPL"`, and `"AAPL"` before `"B"`. - `side` is `"B"` or `"S"`. - A share-based `quantity` is an integer from 1 to `10^7` (0.01 to 100,000 shares), written without leading zeros. - A dollar-based `quantity` is the `$` character followed by an integer amount from 1 to `10^7` cents, written without leading zeros. For every dollar-based order, `amount * 100` is divisible by `price`, so the order converts to a whole, positive number of hundredths of a share. - `price` is an integer from 1 to `10^7` cents, written without leading zeros. - Every starting `amount` in `inventory` is an integer from 0 to 99, written without leading zeros (`"0"` for zero). - Every value, including `amount * 100` for a dollar-based order and every intermediate inventory, is at most `10^9 + 99`, which fits in a 32-bit signed integer. ### Examples **Example 1** ```text Input: orders = [["AAPL", "B", "42", "100"]], inventory = [["AAPL", "99"]] Output: [["AAPL", "57"]] ``` The customer buys 0.42 shares. The inventory of 0.99 shares covers them, leaving 0.99 - 0.42 = 0.57 shares. **Example 2** ```text Input: orders = [["AAPL", "B", "$42", "100"]], inventory = [["AAPL", "50"]] Output: [["AAPL", "8"]] ``` The order is for \$0.42 at \$1.00 per share, which is `42 * 100 / 100 = 42` hundredths, or 0.42 shares. The inventory of 0.50 shares covers them, leaving 0.08 shares, written as `"8"`. **Example 3** ```text Input: orders = [["AAPL", "B", "150", "100"], ["AAPL", "B", "40", "100"], ["AAPL", "B", "50", "100"], ["TSLA", "S", "75", "200"], ["TSLA", "S", "50", "200"], ["TSLA", "B", "$300", "200"]], inventory = [["MSFT", "20"]] Output: [["AAPL", "60"], ["MSFT", "20"], ["TSLA", "75"]] ``` - AAPL starts at 0. Buying 1.50 shares needs 2 whole shares from the exchange and leaves 0.50. Buying 0.40 leaves 0.10. Buying 0.50 needs 1 more whole share and leaves 0.10 + 1.00 - 0.50 = 0.60. - TSLA starts at 0. Selling 0.75 gives 0.75. Selling 0.50 gives 1.25, so 1 whole share is sold on the exchange, leaving 0.25. The last order is for \$3.00 at \$2.00 per share, which is `300 * 100 / 200 = 150` hundredths, or 1.50 shares. The inventory covers only 0.25, so the brokerage buys 2 whole shares and keeps 0.25 + 2.00 - 1.50 = 0.75. - MSFT has no orders and stays at 0.20.

Overview: A coding question about a brokerage that fills fractional-share orders from a small inventory while the exchange trades only whole shares. Given buy and sell orders in hundredths of a share or in dollars, plus the starting inventory, compute each stock's final inventory, which must stay non-negative and below one share after every order.

Read the full Robinhood Software Engineer interview experience this question came from

A brokerage lets its customers buy and sell fractional shares, but the exchange trades only whole shares. To bridge the gap, the brokerage keeps a small inventory of each stock, fills fractional orders from that inventory, and trades whole shares on the exchange only when it has to. For example, suppose the brokerage holds no shares of a stock. A customer buys 1.5 shares, so the brokerage buys 2 whole shares on the exchange, delivers 1.5 and keeps 0.5. A second customer buys 0.4 shares, which come out of inventory and leave 0.1. A third customer buys 0.5 shares; inventory covers only 0.1 of them, so the brokerage buys 1 more whole share and is left with 0.6. Sales work in the other direction: if one customer sells 0.75 shares and another sells 0.5, inventory reaches 1.25, so the brokerage sells 1 whole share on the exchange and keeps 0.25. After every order, the brokerage flattens its inventory of that stock: the inventory is never negative, and it is always less than one share. Given the orders, in the order they arrive, and the brokerage's starting inventory, implement `final_inventory(orders, inventory)` to return its inventory of every stock after all orders have been handled. Each order is `[symbol, side, quantity, price]` and each inventory row is `[symbol, amount]`; every field is a string. Every number is an integer string in hundredths: its last two digits are the decimal part, so `"1000"` means 10.00, `"20"` means 0.20 and `"100"` means 1.00. **Rules** - `side` is `"B"` when the customer buys from the brokerage and `"S"` when the customer sells to it. - `price` is the current price of one share in hundredths of a dollar (cents). It is used only to convert dollar-based orders. - A `quantity` made only of digits is a number of shares in hundredths: `"42"` is 0.42 shares. - A `quantity` that starts with the character `$` is a dollar-based order: the digits after that character are a dollar amount in cents, and the order is for `amount * 100 / price` hundredths of a share (see Example 1). - Orders are handled one at a time, in list order, and each order changes only the inventory of its own `symbol`. A symbol that is not in `inventory` starts at 0. - **Buy of `q` hundredths.** If the inventory is at least `q`, the customer's shares come out of the inventory. Otherwise the brokerage first buys on the exchange the smallest number of whole shares that makes the inventory at least `q`, then delivers `q` from it. - **Sell of `q` hundredths.** `q` is added to the inventory. The brokerage then sells on the exchange as many whole shares as it can without making the inventory negative. - These rules leave every symbol's inventory between 0 and 99 hundredths, inclusive, after every order. **Output** Return one `[symbol, amount]` row for every symbol that appears in `inventory` or in at least one order, including symbols whose final inventory is 0, sorted by `symbol` in ascending order. `amount` is the final inventory in hundredths of a share, written as a base-10 integer without leading zeros: `"8"`, not `"08"`, and `"0"` for an empty inventory. Return an empty list when both inputs are empty. **Constraints** - `0 <= len(orders) <= 10^5` - `0 <= len(inventory) <= 10^4` - Every `symbol` is 1 to 5 uppercase English letters, and a symbol appears at most once in `inventory`. Symbols are compared as plain strings, so `"AA"` sorts before `"AAPL"`, and `"AAPL"` before `"B"`. - `side` is `"B"` or `"S"`. - A share-based `quantity` is an integer from 1 to `10^7` (0.01 to 100,000 shares), written without leading zeros. - A dollar-based `quantity` is the `$` character followed by an integer amount from 1 to `10^7` cents, written without leading zeros. For every dollar-based order, `amount * 100` is divisible by `price`, so the order converts to a whole, positive number of hundredths of a share. - `price` is an integer from 1 to `10^7` cents, written without leading zeros. - Every starting `amount` in `inventory` is an integer from 0 to 99, written without leading zeros (`"0"` for zero). - Every value, including `amount * 100` for a dollar-based order and every intermediate inventory, is at most `10^9 + 99`, which fits in a 32-bit signed integer: no value exceeds 2^31 - 1. **Example 1** ```text Input: orders = [["AAPL", "B", "$42", "100"]], inventory = [["AAPL", "50"]] Output: [["AAPL", "8"]] ``` The order is for $0.42 at $1.00 per share, which is `42 * 100 / 100 = 42` hundredths, or 0.42 shares. The inventory of 0.50 shares covers them, leaving 0.08 shares, written as `"8"`. **Example 2** ```text Input: orders = [["AAPL", "B", "150", "100"], ["AAPL", "B", "40", "100"], ["AAPL", "B", "50", "100"], ["TSLA", "S", "75", "200"], ["TSLA", "S", "50", "200"], ["TSLA", "B", "$300", "200"]], inventory = [["MSFT", "20"]] Output: [["AAPL", "60"], ["MSFT", "20"], ["TSLA", "75"]] ``` - AAPL starts at 0. Buying 1.50 shares needs 2 whole shares from the exchange and leaves 0.50. Buying 0.40 leaves 0.10. Buying 0.50 needs 1 more whole share and leaves 0.10 + 1.00 - 0.50 = 0.60. - TSLA starts at 0. Selling 0.75 gives 0.75. Selling 0.50 gives 1.25, so 1 whole share is sold on the exchange, leaving 0.25. The last order is for $3.00 at $2.00 per share, which is `300 * 100 / 200 = 150` hundredths, or 1.50 shares. The inventory covers only 0.25, so the brokerage buys 2 whole shares and keeps 0.25 + 2.00 - 1.50 = 0.75. - MSFT has no orders and stays at 0.20.

Constraints

  • 0 <= len(orders) <= 10^5
  • 0 <= len(inventory) <= 10^4
  • Every symbol is 1 to 5 uppercase English letters, and a symbol appears at most once in inventory. Symbols are compared as plain strings, so "AA" sorts before "AAPL", and "AAPL" before "B".
  • side is "B" or "S".
  • A share-based quantity is an integer from 1 to 10^7 (0.01 to 100,000 shares), written without leading zeros.
  • A dollar-based quantity is the $ character followed by an integer amount from 1 to 10^7 cents, written without leading zeros. For every dollar-based order, amount * 100 is divisible by price, so the order converts to a whole, positive number of hundredths of a share.
  • price is an integer from 1 to 10^7 cents, written without leading zeros.
  • Every starting amount in inventory is an integer from 0 to 99, written without leading zeros ("0" for zero).
  • Every value, including amount * 100 for a dollar-based order and every intermediate inventory, is at most 10^9 + 99, which fits in a 32-bit signed integer.

Examples

Input: ([['AAPL', 'B', '42', '100']], [['AAPL', '99']])

Expected Output: [['AAPL', '57']]

Explanation: Source Example 1: inventory 99 covers a 42-hundredth buy, leaving 99 - 42 = 57.

Input: ([['AAPL', 'B', '$42', '100']], [['AAPL', '50']])

Expected Output: [['AAPL', '8']]

Explanation: Source Example 2: '$42' at price 100 is 42 * 100 / 100 = 42 hundredths; 50 - 42 = 8, written without a leading zero.

Hints

  1. Each order changes only its own symbol's inventory, so one running amount per symbol, updated in list order, is all the state you need.
  2. Turn every quantity into a whole number of hundredths before applying it: a digits-only quantity already is one and ignores the price, while a '$' quantity is a cent amount that must be converted with the order's price.
  3. Stay in integer hundredths throughout. For each order, ask how many whole shares (100 hundredths each) the exchange must supply or absorb so the inventory ends between 0 and 99.

Loading coding console...

Show the approach

Approach

Keep one running amount, in hundredths of a share, per symbol in a map seeded from inventory; a symbol first seen in an order starts at 0. Process the orders in list order. First convert the quantity to hundredths q: a digits-only quantity is used as is and its price is ignored, while a '$' quantity is amount * 100 / price, which is an exact integer because divisibility is guaranteed. For a buy with current amount cur < q, the smallest number of whole shares k with cur + 100k >= q is ceil((q - cur) / 100) = (q - cur + 99) // 100; add 100k, then deliver q. If cur >= q, the shares simply come out of inventory. For a sell, add q; the largest number of whole shares that can be sold without going negative is floor(cur / 100), which leaves cur mod 100. Invariant: after every order each amount lies in [0, 99]. For a buy, minimality of k means cur + 100(k - 1) < q, so the remainder cur + 100k - q is below 100; for a sell, a remainder mod 100 is in [0, 99]. Equivalently, every order maps cur to (cur - q) mod 100 or (cur + q) mod 100. Finally emit [symbol, decimal string of amount] for every symbol in the map, sorted by plain string comparison. Edge cases: both inputs empty give []; symbols whose final amount is 0 are still listed as '0'; inventory-only symbols are returned unchanged; a buy exactly equal to the inventory needs no exchange purchase; when q - cur is an exact multiple of 100 the ceiling must not add an extra share. Every value stays at most 10^9 + 99, inside 32-bit range; the Java and C++ references use 64-bit arithmetic anyway.

Time complexity:
O((n + m) log k), for n orders, m inventory rows and k distinct symbols
Space complexity:
O(k)