title: Simplify Path
slug: simplify-path
difficulty: medium
leetcode_id: 71
leetcode_url: https://leetcode.com/problems/simplify-path/
categories:
  - strings
  - stack
patterns:
  - monotonic-stack

function_signature: "def simplify_path(path: str) -> str:"

test_cases:
  visible:
    - input: { path: "/home/" }
      expected: "/home"
    - input: { path: "/home//foo/" }
      expected: "/home/foo"
    - input: { path: "/home/user/Documents/../Pictures" }
      expected: "/home/user/Pictures"
  hidden:
    - input: { path: "/../" }
      expected: "/"
    - input: { path: "/.../a/../b/c/../d/./" }
      expected: "/.../b/d"
    - input: { path: "/a/./b/../../c/" }
      expected: "/c"
    - input: { path: "/" }
      expected: "/"
    - input: { path: "/a//b////c/d//././/.." }
      expected: "/a/b/c"
    - input: { path: "/..hidden" }
      expected: "/..hidden"

description: |
  You are given an *absolute* path for a Unix-style file system, which always begins with a slash `'/'`. Your task is to transform this absolute path into its **simplified canonical path**.

  The *rules* of a Unix-style file system are as follows:

  - A single period `'.'` represents the current directory.
  - A double period `'..'` represents the previous/parent directory.
  - Multiple consecutive slashes such as `'//'` and `'///'` are treated as a single slash `'/'`.
  - Any sequence of periods that does **not match** the rules above should be treated as a **valid directory or file name**. For example, `'...'` and `'....'` are valid directory or file names.

  The simplified canonical path should follow these *rules*:

  - The path must start with a single slash `'/'`.
  - Directories within the path must be separated by exactly one slash `'/'`.
  - The path must not end with a slash `'/'`, unless it is the root directory.
  - The path must not have any single or double periods (`'.'` and `'..'`) used to denote current or parent directories.

  Return the **simplified canonical path**.

constraints: |
  - `1 <= path.length <= 3000`
  - `path` consists of English letters, digits, period `'.'`, slash `'/'` or `'_'`.
  - `path` is a valid absolute Unix path.

examples:
  - input: 'path = "/home/"'
    output: '"/home"'
    explanation: "The trailing slash should be removed."
  - input: 'path = "/home//foo/"'
    output: '"/home/foo"'
    explanation: "Multiple consecutive slashes are replaced by a single one."
  - input: 'path = "/home/user/Documents/../Pictures"'
    output: '"/home/user/Pictures"'
    explanation: 'A double period ".." refers to the directory up a level (the parent directory).'
  - input: 'path = "/../"'
    output: '"/"'
    explanation: "Going one level up from the root directory is not possible."
  - input: 'path = "/.../a/../b/c/../d/./"'
    output: '"/.../b/d"'
    explanation: '"..." is a valid name for a directory in this problem.'

explanation:
  intuition: |
    Think of navigating a file system in a terminal. When you type `cd ..`, you go up one directory. When you type `cd .`, you stay in the same directory. And when you type `cd foldername`, you enter that folder.

    The key insight is that a **stack** perfectly models directory navigation:
    - Entering a directory = **push** the directory name onto the stack
    - Going up a level (`..`) = **pop** from the stack (if not empty)
    - Staying in place (`.`) = do nothing

    Imagine the stack as your "breadcrumb trail" of directories from root to your current location. Each valid directory name adds a breadcrumb; `..` removes the last breadcrumb; `.` keeps things unchanged.

    After processing all parts of the path, the stack contains exactly the directories in the canonical path, from root to deepest level. Join them with `'/'` and prepend a `'/'` to get the answer.

  approach: |
    We solve this using a **Stack-Based Path Simplification**:

    **Step 1: Split the path into components**

    - Split the input path by `'/'` to get individual components
    - This automatically handles multiple consecutive slashes — they produce empty strings which we'll filter out

    &nbsp;

    **Step 2: Process each component with a stack**

    - Initialise an empty stack to track valid directory names
    - For each component:
      - If it's `'..'`: pop from the stack (if not empty) — go up one level
      - If it's `'.'` or empty string: skip — stay in current directory
      - Otherwise: push onto the stack — it's a valid directory name

    &nbsp;

    **Step 3: Reconstruct the canonical path**

    - Join all elements in the stack with `'/'`
    - Prepend `'/'` to ensure the path starts with root
    - If the stack is empty, return `'/'` (the root directory)

    &nbsp;

    Example walkthrough with `"/home/user/Documents/../Pictures"`:
    - Split: `['', 'home', 'user', 'Documents', '..', 'Pictures']`
    - Process: `''` skip → `'home'` push → `'user'` push → `'Documents'` push → `'..'` pop → `'Pictures'` push
    - Stack: `['home', 'user', 'Pictures']`
    - Result: `'/home/user/Pictures'`

  common_pitfalls:
    - title: Treating All Periods as Special
      description: |
        Only `'.'` (single period) and `'..'` (double period) have special meaning. Sequences like `'...'`, `'....'`, or `'.hidden'` are **valid directory names**.

        Don't use regex like `/\.+/` to match all period sequences — check for exact matches.
      wrong_approach: "Treating '...' or '....' as special navigation"
      correct_approach: "Only '.' and '..' are special; others are valid names"

    - title: Popping from Empty Stack
      description: |
        When encountering `'..'` at the root level, there's nothing to pop. Trying to go above root is a no-op in Unix.

        For example, `'/../'` should return `'/'`, not cause an error.
      wrong_approach: "stack.pop() without checking if stack is empty"
      correct_approach: "if stack: stack.pop()"

    - title: Forgetting the Leading Slash
      description: |
        The canonical path must start with `'/'`. After joining stack elements, don't forget to prepend it.

        `['home', 'foo']` should become `'/home/foo'`, not `'home/foo'`.
      wrong_approach: "'/'.join(stack)"
      correct_approach: "'/' + '/'.join(stack)"

    - title: Trailing Slash in Result
      description: |
        The canonical path must not end with a slash (unless it's just `'/'`).

        Be careful not to add a trailing slash when reconstructing the path.
      wrong_approach: "'/' + '/'.join(stack) + '/'"
      correct_approach: "'/' + '/'.join(stack)"

  key_takeaways:
    - "**Stack for hierarchical navigation**: Stacks naturally model push/pop operations like directory traversal"
    - "**Split to simplify**: Splitting by delimiter handles multiple consecutive delimiters elegantly"
    - "**Only exact matches are special**: `'.'` and `'..'` are special; everything else (including `'...'`) is a valid name"
    - "**Real-world application**: This is exactly how `os.path.normpath()` or `realpath` work under the hood"

  time_complexity: "O(n). We iterate through the path once to split it, then process each component once."
  space_complexity: "O(n). In the worst case, the stack stores all directory names from the path."

solutions:
  - approach_name: Stack-Based Simplification
    is_optimal: true
    code: |
      def simplify_path(path: str) -> str:
          # Split by '/' — consecutive slashes produce empty strings
          components = path.split('/')
          stack = []

          for component in components:
              if component == '..':
                  # Go up one level (if possible)
                  if stack:
                      stack.pop()
              elif component == '.' or component == '':
                  # Current directory or empty — skip
                  continue
              else:
                  # Valid directory name — add to path
                  stack.append(component)

          # Reconstruct canonical path with leading slash
          return '/' + '/'.join(stack)
    explanation: |
      **Time Complexity:** O(n) — We process each character of the path a constant number of times (split + iteration).

      **Space Complexity:** O(n) — The stack and split result can each hold up to O(n) elements in the worst case.

      We split the path, filter components using a stack (push for valid names, pop for `..`, skip for `.` and empty), then join with slashes. The leading `'/'` ensures proper format.

  - approach_name: Using Built-in (Python)
    is_optimal: false
    code: |
      import os

      def simplify_path(path: str) -> str:
          # os.path.normpath handles path normalisation
          # But it uses OS-specific separators, so ensure Unix style
          normalised = os.path.normpath(path)
          # On Windows, replace backslashes with forward slashes
          return normalised.replace('\\', '/')
    explanation: |
      **Time Complexity:** O(n) — `normpath` processes the path linearly.

      **Space Complexity:** O(n) — Creates a new string for the result.

      This leverages Python's built-in path normalisation. However, it's OS-dependent (uses `\\` on Windows) and may not match LeetCode's expected behaviour exactly. The manual stack approach is more portable and demonstrates the algorithm. Included here to show the real-world equivalent.
