Facebook Pixel

388. Longest Absolute File Path

Problem Description

This problem asks you to find the length of the longest absolute file path in a file system represented as a string.

The file system is represented in a special text format where:

  • Each line represents either a directory or a file
  • The depth level is indicated by tab characters (\t) at the beginning of each line
  • Files are distinguished from directories by containing a dot (.) in their name
  • The actual string uses \n for newlines and \t for tabs

For example, the string "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" represents:

dir
    subdir1
        file1.ext
        subsubdir1
    subdir2
        subsubdir2
            file2.ext

The absolute path to any file is formed by concatenating all parent directories with / separators. In the example above:

  • Path to file1.ext is "dir/subdir1/file1.ext" (length 20)
  • Path to file2.ext is "dir/subdir2/subsubdir2/file2.ext" (length 32)

You need to return the length of the longest absolute path to any file (not directory) in the system. If no files exist, return 0.

The solution uses a stack-based approach:

  1. Parse each line to determine its indentation level (number of tabs) and content
  2. Maintain a stack that tracks the cumulative path length at each directory level
  3. When processing each item:
    • Pop directories from the stack until matching the current indentation level
    • Calculate the current path length (including parent directories and / separators)
    • If it's a directory, push its length to the stack
    • If it's a file (contains .), update the maximum path length
  4. Return the maximum file path length found

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: The file system structure is inherently a tree/graph structure where directories are nodes and the parent-child relationships form edges. Each directory can contain multiple subdirectories and files, creating a hierarchical tree structure.

Is it a tree?

  • Yes: The file system is specifically a tree structure - it has a root directory, each directory/file has exactly one parent (except the root), and there are no cycles. The indentation levels in the input string represent the parent-child relationships in this tree.

DFS

  • Yes: We should use DFS (Depth-First Search) to traverse this tree structure.

Conclusion: The flowchart suggests using DFS for this problem.

The solution implements DFS through a stack-based approach:

  • The stack maintains the current path from root to the current directory level
  • As we parse each line, we're essentially doing a depth-first traversal of the file system tree
  • When we encounter deeper indentation (more tabs), we go deeper into the tree (push to stack)
  • When we encounter shallower indentation (fewer tabs), we backtrack (pop from stack)
  • This mimics the recursive nature of DFS where we explore as deep as possible before backtracking

The DFS pattern is perfect here because:

  1. We need to explore each complete path from root to file
  2. We maintain the current path state as we traverse
  3. We backtrack when moving to sibling branches
  4. The stack naturally handles the path construction and backtracking operations
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

When we look at the file system representation, we notice that the indentation level (number of tabs) tells us exactly where we are in the directory hierarchy. Each tab represents going one level deeper into the tree structure.

The key insight is that as we parse the input line by line, we're essentially performing a depth-first traversal of the file system tree. When we see more tabs than before, we're going deeper into subdirectories. When we see fewer tabs, we're backtracking to a parent directory.

This naturally suggests using a stack to keep track of our current path. Why a stack? Because:

  1. When we go deeper (more tabs), we need to remember where we came from - we push the current directory onto the stack
  2. When we backtrack (fewer tabs), we need to "forget" the directories we're leaving - we pop from the stack
  3. The stack always represents our current path from root to the current level

For calculating path lengths efficiently, instead of storing directory names in the stack, we can store cumulative path lengths. This way:

  • If the stack has [3, 7, 12], it means:
    • First directory has length 3
    • Path to second level is length 7 (including the / separator)
    • Path to third level is length 12

When we encounter a file (identified by having a . in its name), we calculate its full path length by taking the parent's cumulative length from the stack and adding the file's name length plus one for the / separator.

The beauty of this approach is that we don't need to build actual path strings or traverse the tree multiple times. We process each line exactly once, maintaining just enough information (cumulative lengths) to calculate the answer efficiently. The stack naturally handles the tree traversal pattern - growing when we go deeper and shrinking when we backtrack - making this a clean DFS solution.

Solution Approach

The solution implements a stack-based DFS traversal of the file system. Let's walk through the implementation step by step:

1. Initialize Variables:

  • i and n: Track current position and total length of input string
  • ans: Stores the maximum file path length found
  • stk: Stack to maintain cumulative path lengths at each directory level

2. Main Processing Loop: The algorithm processes the input character by character, parsing each line to extract:

  • Indentation level (number of tabs)
  • Content (directory or file name)
  • Whether it's a file (contains a .)

3. Parse Indentation Level:

while input[i] == '\t':
    ident += 1
    i += 1

Count consecutive tab characters to determine the depth level of the current item.

4. Parse Content and Detect Files:

cur, isFile = 0, False
while i < n and input[i] != '\n':
    cur += 1
    if input[i] == '.':
        isFile = True
    i += 1
  • cur: Counts the length of the current directory/file name
  • isFile: Set to True if we encounter a . (indicating a file)

5. Stack Management - Backtracking:

while len(stk) > 0 and len(stk) > ident:
    stk.pop()

This is the backtracking step. If the current indentation level is less than or equal to the stack size, we pop directories from the stack. This happens when we move to a sibling or ancestor directory in the tree.

6. Calculate Cumulative Path Length:

if len(stk) > 0:
    cur += stk[-1] + 1

If there's a parent directory (stack not empty), add its cumulative length plus 1 for the / separator to get the full path length to the current item.

7. Handle Directories vs Files:

if not isFile:
    stk.append(cur)
    continue
ans = max(ans, cur)
  • For directories: Push the cumulative path length onto the stack and continue
  • For files: Update the maximum path length if current is longer

Example Walkthrough: For input "dir\n\tfile.txt":

  1. Process "dir": ident=0, cur=3, not a file → push 3 to stack: [3]
  2. Process "file.txt": ident=1, cur=8, is a file
    • Stack has 1 element, which equals ident, so no popping
    • Add parent length: cur = 8 + 3 + 1 = 12
    • Update answer: ans = 12

The algorithm efficiently handles the tree traversal in O(n) time where n is the input string length, using O(d) space where d is the maximum directory depth.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to illustrate the solution approach.

Input: "dir\n\tsubdir1\n\t\tfile1.ext\n\tsubdir2\n\t\tfile2.ext"

This represents:

dir
    subdir1
        file1.ext
    subdir2
        file2.ext

Step-by-step execution:

Initial state: i=0, ans=0, stk=[]

Line 1: "dir"

  • Parse indentation: ident=0 (no tabs)
  • Parse content: cur=3 (length of "dir"), isFile=False (no dot)
  • Stack management: Stack is empty and equals ident, so no popping
  • Since it's a directory, push to stack: stk=[3]

Line 2: "\tsubdir1"

  • Parse indentation: ident=1 (one tab)
  • Parse content: cur=7 (length of "subdir1"), isFile=False
  • Stack management: len(stk)=1 equals ident, so no popping
  • Calculate path: cur = 7 + stk[-1] + 1 = 7 + 3 + 1 = 11 (adds parent + separator)
  • Push to stack: stk=[3, 11]

Line 3: "\t\tfile1.ext"

  • Parse indentation: ident=2 (two tabs)
  • Parse content: cur=10 (length of "file1.ext"), isFile=True (contains dot)
  • Stack management: len(stk)=2 equals ident, so no popping
  • Calculate path: cur = 10 + stk[-1] + 1 = 10 + 11 + 1 = 22
  • It's a file, so update answer: ans = max(0, 22) = 22
  • Don't push to stack (it's a file)

Line 4: "\tsubdir2"

  • Parse indentation: ident=1 (one tab)
  • Parse content: cur=7 (length of "subdir2"), isFile=False
  • Stack management: len(stk)=2 > ident=1, so pop once: stk=[3]
  • Calculate path: cur = 7 + stk[-1] + 1 = 7 + 3 + 1 = 11
  • Push to stack: stk=[3, 11]

Line 5: "\t\tfile2.ext"

  • Parse indentation: ident=2 (two tabs)
  • Parse content: cur=10 (length of "file2.ext"), isFile=True
  • Stack management: len(stk)=2 equals ident, so no popping
  • Calculate path: cur = 10 + stk[-1] + 1 = 10 + 11 + 1 = 22
  • Update answer: ans = max(22, 22) = 22

Result: The longest absolute file path has length 22

The actual paths are:

  • "dir/subdir1/file1.ext" → length 22
  • "dir/subdir2/file2.ext" → length 22

Notice how the stack grows and shrinks as we traverse the directory tree, always maintaining the cumulative path lengths up to the current level. When we backtrack from subdir1 to process subdir2, we pop from the stack, effectively "forgetting" the subdir1 branch and preparing to explore the subdir2 branch.

Solution Implementation

1class Solution:
2    def lengthLongestPath(self, input: str) -> int:
3        i, n = 0, len(input)
4        max_length = 0
5        # Stack to store cumulative lengths at each directory level
6        path_stack = []
7      
8        while i < n:
9            # Count the indentation level (number of tabs)
10            indent_level = 0
11            while i < n and input[i] == '\t':
12                indent_level += 1
13                i += 1
14          
15            # Get the current file/directory name length and check if it's a file
16            current_length = 0
17            is_file = False
18            while i < n and input[i] != '\n':
19                current_length += 1
20                if input[i] == '.':
21                    is_file = True
22                i += 1
23          
24            # Skip the newline character
25            if i < n:
26                i += 1
27          
28            # Pop directories from stack that are deeper than current indent level
29            while len(path_stack) > indent_level:
30                path_stack.pop()
31          
32            # Calculate cumulative path length including parent directories
33            if path_stack:
34                # Add parent path length plus 1 for the '/' separator
35                current_length += path_stack[-1] + 1
36          
37            # If it's a directory, push its cumulative length to stack
38            if not is_file:
39                path_stack.append(current_length)
40            else:
41                # If it's a file, update the maximum path length
42                max_length = max(max_length, current_length)
43      
44        return max_length
45
1class Solution {
2    public int lengthLongestPath(String input) {
3        int currentIndex = 0;
4        int inputLength = input.length();
5        int maxPathLength = 0;
6      
7        // Stack stores cumulative lengths at each directory level
8        Deque<Integer> directoryLengthStack = new ArrayDeque<>();
9      
10        while (currentIndex < inputLength) {
11            // Count the indentation level (number of tabs)
12            int indentationLevel = 0;
13            while (currentIndex < inputLength && input.charAt(currentIndex) == '\t') {
14                indentationLevel++;
15                currentIndex++;
16            }
17          
18            // Calculate current file/directory name length and check if it's a file
19            int currentNameLength = 0;
20            boolean isFile = false;
21            while (currentIndex < inputLength && input.charAt(currentIndex) != '\n') {
22                currentNameLength++;
23                if (input.charAt(currentIndex) == '.') {
24                    isFile = true;
25                }
26                currentIndex++;
27            }
28          
29            // Skip the newline character
30            currentIndex++;
31          
32            // Pop directories from stack that are at same or deeper level
33            // This happens when we move to a sibling or parent directory
34            while (!directoryLengthStack.isEmpty() && directoryLengthStack.size() > indentationLevel) {
35                directoryLengthStack.pop();
36            }
37          
38            // Calculate cumulative path length including parent directories
39            int cumulativePathLength = currentNameLength;
40            if (!directoryLengthStack.isEmpty()) {
41                // Add parent's cumulative length plus 1 for the '/' separator
42                cumulativePathLength += directoryLengthStack.peek() + 1;
43            }
44          
45            // If it's a directory, push its cumulative length to stack for future use
46            if (!isFile) {
47                directoryLengthStack.push(cumulativePathLength);
48            } else {
49                // If it's a file, update the maximum path length
50                maxPathLength = Math.max(maxPathLength, cumulativePathLength);
51            }
52        }
53      
54        return maxPathLength;
55    }
56}
57
1class Solution {
2public:
3    int lengthLongestPath(string input) {
4        int index = 0;
5        int inputLength = input.size();
6        int maxPathLength = 0;
7        stack<int> pathLengthStack;  // Stack to store cumulative path lengths at each directory level
8      
9        while (index < inputLength) {
10            // Count the indentation level (number of tabs)
11            int indentLevel = 0;
12            while (index < inputLength && input[index] == '\t') {
13                indentLevel++;
14                index++;
15            }
16          
17            // Calculate the length of current file/directory name
18            int currentNameLength = 0;
19            bool isFile = false;
20            while (index < inputLength && input[index] != '\n') {
21                currentNameLength++;
22                // Check if current entry is a file (contains a dot)
23                if (input[index] == '.') {
24                    isFile = true;
25                }
26                index++;
27            }
28          
29            // Skip the newline character
30            index++;
31          
32            // Pop directories from stack that are deeper than current indent level
33            // This happens when we move back up in the directory tree
34            while (!pathLengthStack.empty() && pathLengthStack.size() > indentLevel) {
35                pathLengthStack.pop();
36            }
37          
38            // Calculate cumulative path length including parent directories
39            int cumulativePathLength = currentNameLength;
40            if (!pathLengthStack.empty()) {
41                // Add parent path length plus 1 for the separator '/'
42                cumulativePathLength += pathLengthStack.top() + 1;
43            }
44          
45            // If it's a directory, push its cumulative length to stack for future use
46            if (!isFile) {
47                pathLengthStack.push(cumulativePathLength);
48                continue;
49            }
50          
51            // If it's a file, update the maximum path length
52            maxPathLength = max(maxPathLength, cumulativePathLength);
53        }
54      
55        return maxPathLength;
56    }
57};
58
1function lengthLongestPath(input: string): number {
2    let index = 0;
3    const inputLength = input.length;
4    let maxPathLength = 0;
5    const pathLengthStack: number[] = [];  // Stack to store cumulative path lengths at each directory level
6  
7    while (index < inputLength) {
8        // Count the indentation level (number of tabs)
9        let indentLevel = 0;
10        while (index < inputLength && input[index] === '\t') {
11            indentLevel++;
12            index++;
13        }
14      
15        // Calculate the length of current file/directory name
16        let currentNameLength = 0;
17        let isFile = false;
18        while (index < inputLength && input[index] !== '\n') {
19            currentNameLength++;
20            // Check if current entry is a file (contains a dot)
21            if (input[index] === '.') {
22                isFile = true;
23            }
24            index++;
25        }
26      
27        // Skip the newline character
28        index++;
29      
30        // Pop directories from stack that are deeper than current indent level
31        // This happens when we move back up in the directory tree
32        while (pathLengthStack.length > 0 && pathLengthStack.length > indentLevel) {
33            pathLengthStack.pop();
34        }
35      
36        // Calculate cumulative path length including parent directories
37        let cumulativePathLength = currentNameLength;
38        if (pathLengthStack.length > 0) {
39            // Add parent path length plus 1 for the separator '/'
40            cumulativePathLength += pathLengthStack[pathLengthStack.length - 1] + 1;
41        }
42      
43        // If it's a directory, push its cumulative length to stack for future use
44        if (!isFile) {
45            pathLengthStack.push(cumulativePathLength);
46            continue;
47        }
48      
49        // If it's a file, update the maximum path length
50        maxPathLength = Math.max(maxPathLength, cumulativePathLength);
51    }
52  
53    return maxPathLength;
54}
55

Time and Space Complexity

Time Complexity: O(n) where n is the length of the input string.

The algorithm iterates through each character in the input string exactly once using the while loop with index i. For each line in the input:

  • Counting tabs: O(t) where t is the number of tabs (indentation level)
  • Reading the file/directory name: O(m) where m is the length of the name
  • Stack operations (pop): In the worst case, we might pop all elements, but each element is pushed and popped at most once throughout the entire execution

Since each character is visited once and each stack operation is amortized O(1) over the entire execution, the total time complexity is O(n).

Space Complexity: O(d) where d is the maximum depth of the directory structure.

The space is primarily used by the stack stk which stores the cumulative lengths at each directory level. In the worst case, the stack size equals the maximum depth of the nested directory structure. Additional variables (i, n, ans, ident, cur, isFile) use O(1) space.

Therefore, the overall space complexity is O(d), which in the worst case could be O(n) if the input represents a deeply nested structure with single-character names.

Common Pitfalls

1. Incorrectly Handling Stack Comparison with Indentation Level

One of the most common mistakes is misunderstanding when to pop from the stack. The condition while len(stk) > indent_level is crucial but often implemented incorrectly.

Incorrect Implementation:

# Wrong: using >= instead of >
while len(stk) >= indent_level:
    stk.pop()

Why it's wrong: The stack size represents the number of parent directories, and the indent level represents the depth of the current item. If indent_level = 1, it means the current item is at depth 1 (has one parent), so we should keep exactly 1 item in the stack.

Correct Implementation:

while len(stk) > indent_level:
    stk.pop()

2. Forgetting to Add Path Separator Length

Another frequent error is forgetting to account for the / separator when calculating cumulative path lengths.

Incorrect Implementation:

if len(stk) > 0:
    cur += stk[-1]  # Missing the +1 for '/'

Correct Implementation:

if len(stk) > 0:
    cur += stk[-1] + 1  # Add 1 for the '/' separator

3. Not Handling Edge Cases Properly

Edge Case 1: No files in the system

# Input: "dir\n\tsubdir1\n\tsubdir2"
# Should return 0, not some directory length

Edge Case 2: Files at root level (no indentation)

# Input: "file.txt"
# Should return 8, not 0

Edge Case 3: Multiple dots in filename

# Input: "file.name.ext"
# Should be recognized as a file (any dot counts)

4. Incorrect File Detection

Incorrect Implementation:

# Wrong: Only checking if name ends with common extensions
if name.endswith('.txt') or name.endswith('.ext'):
    isFile = True

Correct Implementation:

# Any dot in the name indicates a file
if '.' in name:
    isFile = True

5. Off-by-One Errors in String Parsing

Be careful with index management when parsing:

Incorrect Implementation:

# Forgetting to skip the newline character
while i < n and input[i] != '\n':
    cur += 1
    i += 1
# Missing: i += 1 to skip '\n'

Correct Implementation:

while i < n and input[i] != '\n':
    cur += 1
    i += 1
# Skip the newline if it exists
if i < n:
    i += 1

Complete Corrected Solution:

class Solution:
    def lengthLongestPath(self, input: str) -> int:
        i, n = 0, len(input)
        max_length = 0
        path_stack = []
      
        while i < n:
            # Count indentation
            indent_level = 0
            while i < n and input[i] == '\t':
                indent_level += 1
                i += 1
          
            # Parse name and check for file
            current_length = 0
            is_file = False
            while i < n and input[i] != '\n':
                current_length += 1
                if input[i] == '.':
                    is_file = True
                i += 1
          
            # Skip newline
            if i < n:
                i += 1
          
            # Maintain stack at correct level
            while len(path_stack) > indent_level:
                path_stack.pop()
          
            # Add parent path length if exists
            if path_stack:
                current_length += path_stack[-1] + 1
          
            # Update based on type
            if not is_file:
                path_stack.append(current_length)
            else:
                max_length = max(max_length, current_length)
      
        return max_length
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using the following tree as input?

1def serialize(root):
2    res = []
3    def dfs(root):
4        if not root:
5            res.append('x')
6            return
7        res.append(root.val)
8        dfs(root.left)
9        dfs(root.right)
10    dfs(root)
11    return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4    StringJoiner res = new StringJoiner(" ");
5    serializeDFS(root, res);
6    return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10    if (root == null) {
11        result.add("x");
12        return;
13    }
14    result.add(Integer.toString(root.val));
15    serializeDFS(root.left, result);
16    serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2    let res = [];
3    serialize_dfs(root, res);
4    return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8    if (!root) {
9        res.push("x");
10        return;
11    }
12    res.push(root.val);
13    serialize_dfs(root.left, res);
14    serialize_dfs(root.right, res);
15}
16

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More