Design file deduplication across nested directories
Company: Anthropic
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates a candidate's competency in file system traversal, robust I/O handling including symbolic links and cycles, content-based deduplication, and algorithmic efficiency within the Coding & Algorithms domain.
Constraints
- 1 <= len(fs) <= 10^5
- The file system graph may contain cycles due to symbolic links or repeated child references
- File contents are strings
- The total length of all reachable file contents is at most 10^6 characters
- Broken symlinks may appear and should be ignored
Examples
Input: ({'/': {'type': 'dir', 'children': ['/docs', '/img', '/readme.txt']}, '/docs': {'type': 'dir', 'children': ['/docs/a.txt', '/docs/b.txt']}, '/img': {'type': 'dir', 'children': ['/img/pic.bin']}, '/readme.txt': {'type': 'file', 'content': 'hello'}, '/docs/a.txt': {'type': 'file', 'content': 'alpha'}, '/docs/b.txt': {'type': 'file', 'content': 'hello'}, '/img/pic.bin': {'type': 'file', 'content': 'xyz'}}, '/')
Expected Output: [['/docs/b.txt', '/readme.txt']]
Explanation: Only '/docs/b.txt' and '/readme.txt' have identical content.
Input: ({'/': {'type': 'dir', 'children': ['/A', '/B']}, '/A': {'type': 'dir', 'children': ['/A/f1.txt', '/A/to_root']}, '/B': {'type': 'dir', 'children': ['/B/f2.txt', '/B/link_to_A']}, '/A/f1.txt': {'type': 'file', 'content': 'same-data'}, '/B/f2.txt': {'type': 'file', 'content': 'same-data'}, '/A/to_root': {'type': 'symlink', 'target': '/'}, '/B/link_to_A': {'type': 'symlink', 'target': '/A'}}, '/')
Expected Output: [['/A/f1.txt', '/B/f2.txt']]
Explanation: The symlinks create cycles back to already visited directories, but the traversal remains safe. The two real files are duplicates.
Input: ({'/': {'type': 'dir', 'children': ['/x.txt', '/alias', '/sub']}, '/x.txt': {'type': 'file', 'content': 'qq'}, '/alias': {'type': 'symlink', 'target': '/x.txt'}, '/sub': {'type': 'dir', 'children': ['/sub/y.txt']}, '/sub/y.txt': {'type': 'file', 'content': 'qq'}}, '/')
Expected Output: [['/sub/y.txt', '/x.txt']]
Explanation: The symlink '/alias' points to the same real file as '/x.txt', so that file is processed once. '/sub/y.txt' is a true duplicate of '/x.txt'.
Input: ({'/': {'type': 'dir', 'children': ['/p', '/q', '/r']}, '/p': {'type': 'file', 'content': 'ABCDEFGHxxIJKLMNOP'}, '/q': {'type': 'file', 'content': 'ABCDEFGHyyIJKLMNOP'}, '/r': {'type': 'file', 'content': 'ABCDEFGHxxIJKLMNOP'}}, '/')
Expected Output: [['/p', '/r']]
Explanation: All three files have the same size and the same prefix/suffix, so the partial check is not enough. The full comparison shows that only '/p' and '/r' are duplicates.
Input: ({'/': {'type': 'dir', 'children': ['/broken']}, '/broken': {'type': 'symlink', 'target': '/missing'}}, '/')
Expected Output: []
Explanation: The only child is a broken symlink, so no files are reachable.
Input: ({'/': {'type': 'dir', 'children': ['/a.txt', '/b.txt', '/d', '/e.txt']}, '/a.txt': {'type': 'file', 'content': '1'}, '/b.txt': {'type': 'file', 'content': '1'}, '/d': {'type': 'dir', 'children': ['/d/c.txt', '/d/f.txt']}, '/d/c.txt': {'type': 'file', 'content': '2'}, '/d/f.txt': {'type': 'file', 'content': '2'}, '/e.txt': {'type': 'file', 'content': '3'}}, '/')
Expected Output: [['/a.txt', '/b.txt'], ['/d/c.txt', '/d/f.txt']]
Explanation: There are two separate duplicate groups, and the final list is sorted by each group's first path.
Hints
- Treat the structure as a graph, not a tree. Keep visited sets for resolved directories and resolved files so symlink cycles do not cause infinite traversal.
- Do not fully compare every file against every other file. First group by size, then by a cheap prefix/suffix signature, and only then compute a full hash for the remaining candidates.