Implement encryption and constant-time random set
Company: Axon
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates understanding of symmetric shared-secret string transformation and the design of a randomized set supporting expected O(1) operations, testing skills in cryptographic string manipulation and data-structure algorithmic reasoning.
Part 1: Repeating-Key Shift Encryption
Constraints
- 0 <= len(s) <= 200000
- 1 <= len(k) <= 200000
- s and k contain only lowercase English letters 'a' to 'z'
Examples
Input: ("abcxyz", "bcd")
Expected Output: "bdfyac"
Explanation: The repeated key is bcdbcd, giving shifts 1, 2, 3, 1, 2, 3. Applying them to abcxyz produces bdfyac.
Input: ("", "abc")
Expected Output: ""
Explanation: An empty plaintext has no characters to encrypt, so the result is an empty string.
Input: ("z", "b")
Expected Output: "a"
Explanation: The key character b means shift by 1. Shifting z by 1 wraps around to a.
Input: ("hello", "a")
Expected Output: "hello"
Explanation: The key character a means shift by 0, so every character stays the same.
Input: ("ace", "zebra")
Expected Output: "zgf"
Explanation: Only the first three key characters are used: z, e, b, which correspond to shifts 25, 4, and 1. So a -> z, c -> g, and e -> f.
Hints
- Convert each character to a number from 0 to 25, apply the shift, then convert back to a character.
- The key repeats, so the shift for position i comes from k[i % len(k)].
Part 2: Simulate a Randomized Set with O(1) Operations
Constraints
- 0 <= len(operations) <= 200000
- -10^9 <= val <= 10^9 for insert/remove operations
- len(picks) equals the number of 'getRandom' operations
- 'getRandom' is never called when the set is empty
Examples
Input: ([('insert', 1), ('insert', 2), ('getRandom',), ('remove', 1), ('insert', 2), ('getRandom',)], [1, 0])
Expected Output: [True, True, 2, True, False, 2]
Explanation: After inserting 1 and 2, the array is [1, 2]. The first getRandom uses 1 % 2 = 1, so it returns 2. Removing 1 swap-deletes it, leaving [2]. Inserting 2 again fails because 2 is already present. The second getRandom uses 0 % 1 = 0, so it returns 2.
Input: ([('remove', 5), ('insert', 5), ('insert', 5), ('getRandom',), ('remove', 5)], [3])
Expected Output: [False, True, False, 5, True]
Explanation: Removing 5 first fails because the set is empty. Inserting 5 succeeds, inserting it again fails, getRandom returns 5 because it is the only element, and removing 5 then succeeds.
Input: ([('insert', 10), ('insert', 20), ('insert', 30), ('remove', 20), ('getRandom',), ('remove', 10), ('getRandom',)], [5, 2])
Expected Output: [True, True, True, True, 30, True, 30]
Explanation: Removing 20 must swap the last element 30 into its position, so the array becomes [10, 30]. The first getRandom uses 5 % 2 = 1 and returns 30. Removing 10 then moves 30 to index 0, leaving [30]. The last getRandom returns 30.
Input: ([('insert', -3), ('insert', -1), ('remove', -3), ('getRandom',), ('insert', -3), ('getRandom',)], [10, 1])
Expected Output: [True, True, True, -1, True, -3]
Explanation: After removing -3, the remaining array is [-1]. The first getRandom returns -1. Inserting -3 again succeeds, making the array [-1, -3]. The second getRandom uses 1 % 2 = 1 and returns -3.
Input: ([], [])
Expected Output: []
Explanation: With no operations, the result is an empty list.
Hints
- Use a list for O(1) random access by index, and a dictionary mapping value -> index for O(1) membership and updates.
- When removing a value from the middle of the list, swap in the last element so you can pop in O(1).