Quick Overview

Allocate unique vault file names with the smallest available numeric suffix, handling preexisting suffixes, repeated requests, and case sensitivity.

Allocate Unique File Names in an In-Memory Vault

Company: Harvey

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

An in-memory file vault must assign a unique name whenever a file is created. If the requested name already exists, append a parenthesized positive integer to obtain an unused name. Implement `allocate_names(requests: string[]) -> string[]` and return the assigned name for each request in order. ### Constraints & Assumptions - All requests target the same namespace, which starts empty. No file is deleted or renamed during the sequence. - If requested name `s` is unused, assign `s` unchanged. - Otherwise choose the smallest positive integer `x` for which `s + "(" + decimal(x) + ")"` is unused, then reserve and return that name. - Names are case-sensitive, nonempty ASCII strings of at most 100 characters. There are at most 10,000 requests. - Parentheses already present in a requested name are literal text. For example, a collision on `a(1)` tries `a(1)(1)`; do not reinterpret it as a collision on `a`. - The smallest available positive suffix and flat namespace are explicit practice choices for the reported duplicate-name behavior. ### Examples ```text requests = ["report", "report", "report", "report(1)"] result = ["report", "report(1)", "report(2)", "report(1)(1)"] ``` ```text requests = ["a(1)", "a", "a", "A"] result = ["a(1)", "a", "a(2)", "A"] ``` ```hint Remember where each repeated base can resume Once a suffix candidate is known to be occupied and names are never removed, trying the same candidate again for that base cannot succeed. ```

Overview: Allocate unique vault file names with the smallest available numeric suffix, handling preexisting suffixes, repeated requests, and case sensitivity.

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

An in-memory file vault must assign a unique name whenever a file is created. If the requested name already exists, append a parenthesized positive integer to obtain an unused name. Implement `allocate_names(requests: string[]) -> string[]` and return the assigned name for each request in order. ### Constraints & Assumptions - All requests target the same namespace, which starts empty. No file is deleted or renamed during the sequence. - If requested name `s` is unused, assign `s` unchanged. - Otherwise choose the smallest positive integer `x` for which `s + "(" + decimal(x) + ")"` is unused, then reserve and return that name. - Names are case-sensitive, nonempty ASCII strings of at most 100 characters. There are at most 10,000 requests. - Parentheses already present in a requested name are literal text. For example, a collision on `a(1)` tries `a(1)(1)`; do not reinterpret it as a collision on `a`. - The smallest available positive suffix and flat namespace are explicit practice choices for the reported duplicate-name behavior. ### Examples ```text requests = ["report", "report", "report", "report(1)"] result = ["report", "report(1)", "report(2)", "report(1)(1)"] ``` ```text requests = ["a(1)", "a", "a", "A"] result = ["a(1)", "a", "a(2)", "A"] ``` ```hint Remember where each repeated base can resume Once a suffix candidate is known to be occupied and names are never removed, trying the same candidate again for that base cannot succeed. ```

Constraints

  • At most 10000 requests in a single initially empty namespace.
  • Requests are nonempty case-sensitive ASCII strings of at most 100 characters.
  • On collision append the smallest available positive decimal integer in parentheses to the entire requested name.
  • No deletions or renames occur; existing parentheses remain literal text.

Examples

Input: (['report', 'report', 'report', 'report(1)'],)

Expected Output: ['report', 'report(1)', 'report(2)', 'report(1)(1)']

Explanation: Reserve each result immediately; a requested parenthesized name is a literal new base.

Input: (['a(1)', 'a', 'a', 'A'],)

Expected Output: ['a(1)', 'a', 'a(2)', 'A']

Explanation: A pre-reserved suffix is skipped, while uppercase A remains a separate name.

Loading coding console...

Show the approach

Approach

Use a hash map whose keys are reserved names and whose value is the next suffix to try when that exact name is requested again. An unused request is reserved unchanged with cursor one. On a collision, test candidates formed by appending a canonical positive decimal suffix to the entire requested text. Advance past occupied names, reserve the first unused candidate, and save the next suffix for the original base. Every suffix below a saved cursor has already been found occupied or allocated; because there is no deletion, those suffixes can never become available. Starting at that cursor therefore still yields the smallest valid positive suffix. Initializing every newly reserved name with its own cursor handles later direct requests for generated names and nested parentheses. A given base never retries a suffix. Across bases, a canonical final parenthesized integer uniquely identifies the base obtained by removing that final component, so an already-reserved spelling can cause at most one failed suffix probe in the entire run. Thus there are O(N) expected hash-table probes over N requests. If L bounds candidate spelling length, construction and hashing give O(NL) expected time and O(NL) stored characters, including output. L is at most the maximum request length plus O(log(N+1)) suffix characters; generated names are not truncated.

Time complexity:
O(NL) expected time, where L bounds constructed name length; O(N) expected hash-table probes.
Space complexity:
O(NL) characters for reserved names and returned output.