Quick Overview

Maintain stable case-insensitive unique column names across additions, removals, reordering, reappearance, and natural suffix collisions, with independent table histories.

Keep Stable Unique Column Names Across Schema Changes

Company: Microsoft

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

Return stable effective column names for repeated snapshots of table schemas. Replace every dot in an original column name with an underscore. Effective names must be unique within a table, ignoring ASCII case. Resolve collisions with suffixes `_1`, `_2`, and so on. Implement `rename_columns(calls: string[][]) -> string[][]`. Each input row is `[tableName, column1, column2, ...]` and describes that table's current ordered column list. Return one array of effective column names per call, in the same order as its columns. ### Constraints & Assumptions - At most 1000 calls and 10000 column occurrences in total. Names contain ASCII letters, digits, dots, and underscores and are nonempty; each name is at most 100 characters. - Table identity and original column identity use exact case-sensitive strings. No original column is repeated within a single call. Different original names differing only in case remain different columns and need distinct effective names. - Each table maintains an independent persistent mapping for all original names ever seen during this function call. Once assigned, an effective name never changes, even if the column disappears, moves, or later returns. - Effective names assigned to absent columns remain reserved. This reservation policy makes the reported reappearance guarantee explicit. - Process newly encountered columns in their input order. The initial candidate is the original name with dots replaced by underscores. If its case-insensitive form is reserved, try that entire candidate plus `_1`, then `_2`, and so on, choosing the first unused suffix. Preserve the candidate's original case in the assigned name. - Natural suffixes are ordinary name text. For example, a new original `a_1` whose candidate is already reserved may become `a_1_1`, not automatically `a_2`. - Calls and returned arrays may contain zero columns. ### Example ```text calls = [["T","a.b","a_b","a_b_1"], ["T","a_b"], ["T","a_b_1","a.b","A.B"], ["U","a_b"]] result = [["a_b","a_b_1","a_b_1_1"], ["a_b_1"], ["a_b_1_1","a_b","A_B_2"], ["a_b"]] ``` Explain why rebuilding names from only the current snapshot breaks stability, and how you handle a natural suffixed name colliding with an earlier generated name. Analyze repeated collision searches rather than assuming every new assignment is constant time. ```hint Keep identity and reservation separate The original-to-effective mapping preserves historical identity. A second case-normalized set answers whether a candidate effective name is already reserved for that table. ```

Overview: Maintain stable case-insensitive unique column names across additions, removals, reordering, reappearance, and natural suffix collisions, with independent table histories.

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

Return stable effective column names for repeated snapshots of table schemas. Replace every dot in an original column name with an underscore. Effective names must be unique within a table, ignoring ASCII case. Resolve collisions with suffixes `_1`, `_2`, and so on. Implement `rename_columns(calls: string[][]) -> string[][]`. Each input row is `[tableName, column1, column2, ...]` and describes that table's current ordered column list. Return one array of effective column names per call, in the same order as its columns. ### Constraints & Assumptions - At most 1000 calls and 10000 column occurrences in total. Names contain ASCII letters, digits, dots, and underscores and are nonempty; each name is at most 100 characters. - Table identity and original column identity use exact case-sensitive strings. No original column is repeated within a single call. Different original names differing only in case remain different columns and need distinct effective names. - Each table maintains an independent persistent mapping for all original names ever seen during this function call. Once assigned, an effective name never changes, even if the column disappears, moves, or later returns. - Effective names assigned to absent columns remain reserved. This reservation policy makes the reported reappearance guarantee explicit. - Process newly encountered columns in their input order. The initial candidate is the original name with dots replaced by underscores. If its case-insensitive form is reserved, try that entire candidate plus `_1`, then `_2`, and so on, choosing the first unused suffix. Preserve the candidate's original case in the assigned name. - Natural suffixes are ordinary name text. For example, a new original `a_1` whose candidate is already reserved may become `a_1_1`, not automatically `a_2`. - Calls and returned arrays may contain zero columns. ### Example ```text calls = [["T","a.b","a_b","a_b_1"], ["T","a_b"], ["T","a_b_1","a.b","A.B"], ["U","a_b"]] result = [["a_b","a_b_1","a_b_1_1"], ["a_b_1"], ["a_b_1_1","a_b","A_B_2"], ["a_b"]] ``` Explain why rebuilding names from only the current snapshot breaks stability, and how you handle a natural suffixed name colliding with an earlier generated name. Analyze repeated collision searches rather than assuming every new assignment is constant time. ```hint Keep identity and reservation separate The original-to-effective mapping preserves historical identity. A second case-normalized set answers whether a candidate effective name is already reserved for that table. ```

Constraints

  • At most 1000 calls and 10000 column occurrences; each call begins with a table name and can contain zero columns.
  • Names have length 1 through 100 and contain ASCII letters, digits, dots and underscores.
  • Table and original identities are case-sensitive. Originals are distinct within a call.
  • Effective names are unique ignoring ASCII case per table. All historical assignments, including absent columns, remain reserved.
  • Process new originals in input order; replace dots with underscores and append the smallest unused suffix to the entire base if needed.
  • Return one ordered array of effective column names per input call.

Examples

Input: ([['T', 'a.b', 'a_b', 'a_b_1'], ['T', 'a_b'], ['T', 'a_b_1', 'a.b', 'A.B'], ['U', 'a_b']],)

Expected Output: [['a_b', 'a_b_1', 'a_b_1_1'], ['a_b_1'], ['a_b_1_1', 'a_b', 'A_B_2'], ['a_b']]

Explanation: Source example checks stable names and independent table state.

Input: ([['T', 'a_1', 'a', 'A', 'A_1']],)

Expected Output: [['a_1', 'a', 'A_2', 'A_1_1']]

Explanation: A natural suffix blocks suffix one and keeps its own entire base.

Loading coding console...

Show the approach

Approach

Keep separate per-table structures: an exact original-to-effective mapping, a set of lowercase reserved effective names, and an optional next-suffix cursor per lowercase base. Existing originals immediately reuse their prior name. For a new original, replace every dot, then choose the unchanged base if free; otherwise test numeric suffixes on that entire base. All reservations persist. A cursor may skip suffixes already tested because those names remain reserved forever; it is updated only after a successful assignment. This preserves the first-unused rule even when a naturally suffixed name reserves a candidate between calls. Each assignment is unique ignoring ASCII case and remains stable by induction over calls. Rebuilding from a current snapshot would release absent names and change identities on reappearance. Let C be column occurrences, U unique originals, L maximum input-name length, and P the total suffix candidates probed. With expected hash-table operations, time is O((C+P)(L+log(U+1))) plus per-call overhead. A single assignment may scan many suffixes; without cursors repeated scans can be quadratic. Cursors avoid rescanning already tried suffixes for the same normalized base, though a conservative overall bound remains O(CU(L+log(U+1))). Stored assignments/reservations use O(U*(L+log(U+1))) characters, plus output.

Time complexity:
O((C + P) * (L + log(U + 1))) expected, where P counts suffix probes; conservative O(C * U * (L + log(U + 1))) bound
Space complexity:
O(U * (L + log(U + 1))) state plus returned names