PracHub
QuestionsLearningGuidesInterview Prep

Quick Overview

This question evaluates understanding of radix trees (prefix-compressed tries), sequence-oriented trie manipulation, and the maintenance of invariants like distinct branching and maximal compression, and belongs to the Coding & Algorithms domain.

  • medium
  • xAI
  • Coding & Algorithms
  • Software Engineer

Implement a Radix Cache for Integer Sequences

Company: xAI

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

Implement a `RadixCache` — a radix tree (prefix-compressed trie) that stores **sequences of integers**. In a plain trie, every node holds exactly one element, so long sequences create long chains of single-child nodes. A radix tree compresses those chains: each edge is labeled with a **non-empty list of integers**, and the tree maintains two invariants at all times: 1. **Distinct branching:** among the children of any node, no two edge labels start with the same integer. 2. **Maximal compression:** an edge label is as long as possible — a chain of nodes is only broken where sequences actually diverge (or where an inserted sequence ends). Your `RadixCache` must support: - `insert(seq)` — insert a non-empty sequence of integers into the tree. - `dump()` — return the current tree structure as a list of strings, one line per node, produced by a depth-first traversal starting from the (unlabeled) root. Each line is the string representation of that node's edge label (e.g. `[1, 2, 3]`), indented with two spaces per depth level. At every node, visit children in **ascending order of the first integer of their edge label**. ### Insertion semantics To insert a sequence, walk down from the root. At the current node, look for a child whose edge label starts with the next integer of the remaining sequence: - **No such child:** create a new child whose edge label is the entire remaining sequence, and stop. - **The remaining sequence fully consumes the edge label:** descend into that child and continue with the rest of the sequence (if nothing remains, the sequence already ends exactly at that node — the structure is unchanged). - **The remaining sequence matches only a proper prefix of the edge label (or ends inside it):** **split** the edge. Create an intermediate node whose incoming edge is the common prefix; the existing child keeps the leftover part of its original label (with all of its subtree intact); if the inserted sequence still has leftover integers, they become a second new child of the intermediate node. Duplicate inserts, and inserts of a sequence that is a prefix of an already-stored sequence, must not corrupt the structure. ### Examples **Example 1** ```python tree = RadixCache() tree.insert([10, 20]) tree.insert([1, 2, 3]) tree.insert([1, 2, 3, 4, 5, 6]) tree.dump() ``` The tree is now: ``` Root ├── [1, 2, 3] │ └── [4, 5, 6] └── [10, 20] ``` so `dump()` returns: ``` ["[1, 2, 3]", " [4, 5, 6]", "[10, 20]"] ``` Note that `[1, 2, 3, 4, 5, 6]` shared the prefix `[1, 2, 3]` with an existing node, so only the new suffix `[4, 5, 6]` was added as a child. **Example 2** (continuing from Example 1) ```python tree.insert([1, 2, 3, 40, 50, 60, 70, 80]) tree.insert([1, 2, 3, 40, 50, 60, 700, 800]) tree.dump() ``` The first insert adds the child `[40, 50, 60, 70, 80]` under `[1, 2, 3]`. The second insert shares only the prefix `[40, 50, 60]` with that edge, so the edge is split: ``` Root ├── [1, 2, 3] │ ├── [4, 5, 6] │ └── [40, 50, 60] │ ├── [70, 80] │ └── [700, 800] └── [10, 20] ``` so `dump()` returns: ``` ["[1, 2, 3]", " [4, 5, 6]", " [40, 50, 60]", " [70, 80]", " [700, 800]", "[10, 20]"] ``` ### Constraints - $1 \le$ number of `insert` calls $\le 10^4$ - $1 \le$ length of each inserted sequence $\le 10^3$ - $0 \le$ each integer $\le 10^9$ - The total number of integers across all inserted sequences does not exceed $10^5$. - `insert` should run in $O(L)$ time for a sequence of length $L$ (excluding any cost of ordering children, which may be paid in `dump()` instead).

Quick Answer: This question evaluates understanding of radix trees (prefix-compressed tries), sequence-oriented trie manipulation, and the maintenance of invariants like distinct branching and maximal compression, and belongs to the Coding & Algorithms domain.

Implement a `RadixCache` — a radix tree (prefix-compressed trie) that stores **sequences of integers**. In a plain trie, every node holds exactly one element, so long sequences create long chains of single-child nodes. A radix tree compresses those chains: each edge is labeled with a **non-empty list of integers**, and the tree maintains two invariants at all times: 1. **Distinct branching:** among the children of any node, no two edge labels start with the same integer. 2. **Maximal compression:** an edge label is as long as possible — a chain of nodes is only broken where sequences actually diverge (or where an inserted sequence ends). Your `RadixCache` must support: - `insert(seq)` — insert a non-empty sequence of integers into the tree. - `dump()` — return the current tree structure as a list of strings, one line per node, produced by a depth-first traversal starting from the (unlabeled) root. Each line is the string representation of that node's edge label (e.g. `[1, 2, 3]`), indented with two spaces per depth level. At every node, visit children in **ascending order of the first integer of their edge label**. ### Insertion semantics To insert a sequence, walk down from the root. At the current node, look for a child whose edge label starts with the next integer of the remaining sequence: - **No such child:** create a new child whose edge label is the entire remaining sequence, and stop. - **The remaining sequence fully consumes the edge label:** descend into that child and continue with the rest of the sequence (if nothing remains, the sequence already ends exactly at that node — the structure is unchanged). - **The remaining sequence matches only a proper prefix of the edge label (or ends inside it):** **split** the edge. Create an intermediate node whose incoming edge is the common prefix; the existing child keeps the leftover part of its original label (with all of its subtree intact); if the inserted sequence still has leftover integers, they become a second new child of the intermediate node. Duplicate inserts, and inserts of a sequence that is a prefix of an already-stored sequence, must not corrupt the structure. ### Harness contract This console drives the cache through a single function `solution(sequences)`: it inserts each sequence in `sequences` (a list of integer lists) in order, then returns `dump()` — the list of indented label strings. ### Example ```python solution([[10, 20], [1, 2, 3], [1, 2, 3, 4, 5, 6], [1, 2, 3, 40, 50, 60, 70, 80], [1, 2, 3, 40, 50, 60, 700, 800]]) # -> # ["[1, 2, 3]", # " [4, 5, 6]", # " [40, 50, 60]", # " [70, 80]", # " [700, 800]", # "[10, 20]"] ``` ### Constraints - 1 ≤ number of `insert` calls ≤ 10^4 - 1 ≤ length of each inserted sequence ≤ 10^3 - 0 ≤ each integer ≤ 10^9 - The total number of integers across all inserted sequences does not exceed 10^5. - `insert` should run in O(L) time for a sequence of length L (excluding any cost of ordering children, which may be paid in `dump()`).

Constraints

  • 1 <= number of insert calls <= 10^4
  • 1 <= length of each inserted sequence <= 10^3
  • 0 <= each integer <= 10^9
  • Total number of integers across all inserted sequences <= 10^5
  • insert should run in O(L) time for a sequence of length L

Examples

Input: ([[10, 20], [1, 2, 3], [1, 2, 3, 4, 5, 6]],)

Expected Output: ['[1, 2, 3]', ' [4, 5, 6]', '[10, 20]']

Explanation: Example 1. [1,2,3,4,5,6] shares the prefix [1,2,3] with an existing node, so only the suffix [4,5,6] is added as a child. Root children are ordered by first integer: 1 before 10.

Input: ([[10, 20], [1, 2, 3], [1, 2, 3, 4, 5, 6], [1, 2, 3, 40, 50, 60, 70, 80], [1, 2, 3, 40, 50, 60, 700, 800]],)

Expected Output: ['[1, 2, 3]', ' [4, 5, 6]', ' [40, 50, 60]', ' [70, 80]', ' [700, 800]', '[10, 20]']

Explanation: Example 2. The two [1,2,3,40,50,60,...] inserts share the prefix [40,50,60], so that edge is split into an intermediate node with children [70,80] and [700,800].

Hints

  1. Key each node's children by the FIRST integer of the child edge label. Because of the distinct-branching invariant, at most one child can start with a given integer, so lookup is O(1).
  2. To insert, at each node compute the length j of the common prefix between the remaining sequence and the matching child's label. If j equals the child's label length, descend and continue; otherwise you must split the edge at position j.
  3. Splitting: make an intermediate node whose incoming edge is label[:j]; the old child keeps label[j:] (and its whole subtree); if the inserted sequence still has leftover integers, add them as a second child of the intermediate node.
  4. A duplicate insert or a prefix-of-existing insert simply runs out of integers (j reaches the end of the sequence) with no new leaf created — handle it by doing nothing extra.
  5. dump() is a DFS; sort each node's children by their first integer only at traversal time so insert stays O(L).
Last updated: Jul 2, 2026

Loading coding console...

PracHub

Master your tech interviews with 8,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Flatten and unflatten nested Python structures - xAI (medium)
  • Compute dasher pay from order events - xAI (medium)
  • Compute total active time per Twitter Space - xAI (medium)
  • Design a Recoverable Iterator - xAI (medium)
  • Implement Distributed Matrix Multiplication - xAI (hard)