Design a constrained in-memory file system
Company: Harvey AI
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates design and implementation skills for hierarchical in-memory data structures, string parsing and file-name collision handling under constraints, and the ability to reason about time and space complexity.
Constraints
- 1 <= len(operations) <= 10^4
- 1 <= len(path) <= 200
- Each operation is either ('addFile', path) or ('listEntries', path)
- Each directory may contain at most 5 immediate children
- Paths are case-sensitive and must be absolute
- Repeated slashes, trailing slashes (except '/'), and '.' / '..' segments are invalid
Examples
Input: [('addFile', '/path/to/somewhere/file.txt'), ('listEntries', '/path/to/somewhere'), ('listEntries', '/path/to'), ('listEntries', '/')]
Expected Output: [['file.txt'], ['somewhere'], ['path']]
Explanation: Intermediate directories are created automatically. The three list queries inspect the deepest directory, its parent, and the root.
Input: [('addFile', '/d/file.txt'), ('addFile', '/d/file.txt'), ('addFile', '/d/file(1).txt'), ('listEntries', '/d')]
Expected Output: [['file(1)(1).txt', 'file(1).txt', 'file.txt']]
Explanation: The second file becomes 'file(1).txt'. Adding 'file(1).txt' again becomes 'file(1)(1).txt'. The listing is sorted lexicographically.
Input: [('addFile', '/a/x'), ('addFile', '/b/x'), ('addFile', '/c/x'), ('addFile', '/d/x'), ('addFile', '/e/x'), ('listEntries', '/'), ('addFile', '/z/x'), ('listEntries', '/'), ('listEntries', '/z')]
Expected Output: [['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e'], []]
Explanation: After creating five top-level directories, the root is full. Adding '/z/x' is rejected atomically, so '/z' does not appear.
Input: [('listEntries', '/'), ('addFile', '/'), ('addFile', 'relative.txt'), ('addFile', '/bad//path/file'), ('addFile', '/trail/'), ('addFile', '/ok/file'), ('addFile', '/ok/file/nested'), ('listEntries', '/ok'), ('listEntries', '/ok/file'), ('listEntries', '/ok/')]
Expected Output: [[], ['file'], [], []]
Explanation: Invalid addFile paths do nothing. '/ok/file/nested' is rejected because 'file' is already a file, not a directory. Listing a file path or an invalid path returns [].
Input: [('addFile', '/cfg/.env'), ('addFile', '/cfg/.env'), ('addFile', '/cfg/archive.tar.gz'), ('addFile', '/cfg/archive.tar.gz'), ('listEntries', '/cfg')]
Expected Output: [['.env', '.env(1)', 'archive.tar(1).gz', 'archive.tar.gz']]
Explanation: '.env' is treated as having no extension, so it becomes '.env(1)'. For 'archive.tar.gz', only the last dot starts the extension, so the duplicate becomes 'archive.tar(1).gz'.
Hints
- A tree (trie-like) structure works well: each directory node maps child names to child nodes, and each node stores whether it is a file or a directory.
- To keep addFile atomic, first validate the path and check all capacity constraints before mutating the structure. For renaming, split on the last dot only if that dot is not the first character.