Solve anagram grouping and in-place allocator
Company: NVIDIA
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This two-part question evaluates algorithmic problem-solving across string grouping and constrained in-place memory allocation, testing string-processing and data-organization skills as well as low-level memory layout reasoning under strict O(1) extra-space limits.
Part 1: Group Anagrams
Constraints
- 0 <= len(strs) <= 100000
- 0 <= len(strs[i]) <= 100
- Each string contains only lowercase English letters
Examples
Input: (['eat', 'tea', 'tan', 'ate', 'nat', 'bat'],)
Expected Output: [['ate', 'eat', 'tea'], ['bat'], ['nat', 'tan']]
Explanation: The words 'eat', 'tea', and 'ate' form one anagram group; 'tan' and 'nat' form another.
Input: ([''],)
Expected Output: [['']]
Explanation: A single empty string is its own anagram group.
Input: ([],)
Expected Output: []
Explanation: Empty input should return no groups.
Input: (['ab', 'ba', 'abc', 'cab', 'bca', 'ab'],)
Expected Output: [['ab', 'ab', 'ba'], ['abc', 'bca', 'cab']]
Explanation: Duplicate words should be preserved in the output.
Hints
- Words that are anagrams share the same canonical signature.
- Because the alphabet is fixed to 26 lowercase letters, a 26-length frequency tuple is a good hash key.
Part 2: Simulate an In-Place Fixed-Size Allocator
Constraints
- 0 <= len(operations) <= 100000
- The pool size is fixed at 32 slots
- 0 <= x <= 18446744073709551615 for allocate operations
- Every free(handle) in the tests is valid and never double-frees a slot
Examples
Input: ([],)
Expected Output: []
Explanation: No operations means no results.
Input: ([('allocate', 10), ('allocate', 20), ('free', 0), ('allocate', 30)],)
Expected Output: [0, 1, -2, 0]
Explanation: After freeing handle 0, the allocator should be able to reuse that slot.
Input: ([('allocate', 5), ('allocate', 6), ('init',), ('allocate', 7)],)
Expected Output: [0, 1, -2, 0]
Explanation: init resets the allocator so the next allocation starts again from slot 0.
Input: ([('allocate', 1), ('allocate', 2), ('allocate', 3), ('free', 1), ('free', 0), ('allocate', 9), ('allocate', 8)],)
Expected Output: [0, 1, 2, -2, -2, 0, 1]
Explanation: Freed slots are reused in LIFO order when using the free-list head as a stack.
Input: ([('allocate', 0), ('allocate', 1), ('allocate', 2), ('allocate', 3), ('allocate', 4), ('allocate', 5), ('allocate', 6), ('allocate', 7), ('allocate', 8), ('allocate', 9), ('allocate', 10), ('allocate', 11), ('allocate', 12), ('allocate', 13), ('allocate', 14), ('allocate', 15), ('allocate', 16), ('allocate', 17), ('allocate', 18), ('allocate', 19), ('allocate', 20), ('allocate', 21), ('allocate', 22), ('allocate', 23), ('allocate', 24), ('allocate', 25), ('allocate', 26), ('allocate', 27), ('allocate', 28), ('allocate', 29), ('allocate', 30), ('allocate', 31), ('allocate', 32)],)
Expected Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, -1]
Explanation: The first 32 allocations succeed, and the 33rd fails because the pool is full.
Hints
- When a slot is free, it does not need to store user data. It can store the index of the next free slot instead.
- Maintain the free slots as a singly linked list or stack using the memory array itself.