title: Decode String
slug: decode-string
difficulty: medium
leetcode_id: 394
leetcode_url: https://leetcode.com/problems/decode-string/
categories:
  - strings
  - stack
  - recursion
patterns:
  - monotonic-stack

description: |
  Given an encoded string, return its decoded string.

  The encoding rule is: `k[encoded_string]`, where the `encoded_string` inside the square brackets is being repeated exactly `k` times. Note that `k` is guaranteed to be a positive integer.

  You may assume that the input string is always valid; there are no extra white spaces, square brackets are well-formed, etc. Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, `k`. For example, there will not be input like `3a` or `2[4]`.

  The test cases are generated so that the length of the output will never exceed `10^5`.

constraints: |
  - `1 <= s.length <= 30`
  - `s` consists of lowercase English letters, digits, and square brackets `'[]'`
  - `s` is guaranteed to be a valid input
  - All the integers in `s` are in the range `[1, 300]`

examples:
  - input: 's = "3[a]2[bc]"'
    output: '"aaabcbc"'
    explanation: "The pattern 3[a] expands to 'aaa' and 2[bc] expands to 'bcbc', giving 'aaabcbc'."
  - input: 's = "3[a2[c]]"'
    output: '"accaccacc"'
    explanation: "The inner 2[c] expands to 'cc', making 3[acc], which expands to 'accaccacc'."
  - input: 's = "2[abc]3[cd]ef"'
    output: '"abcabccdcdcdef"'
    explanation: "2[abc] becomes 'abcabc', 3[cd] becomes 'cdcdcd', plus 'ef' at the end."

explanation:
  intuition: |
    Think of this problem like unpacking nested boxes. Each `k[...]` is a box containing a message that needs to be repeated `k` times. The tricky part is that boxes can be nested inside other boxes — you need to unpack the innermost boxes first before you can complete the outer ones.

    This "last opened, first closed" pattern is exactly what a **stack** excels at. When you encounter an opening bracket `[`, you're entering a new nested level. When you hit a closing bracket `]`, you've completed the innermost pattern and need to expand it before continuing with the outer context.

    Imagine reading `3[a2[c]]`:
    - You see `3[` — push your current work aside, start fresh for what's inside
    - You see `a2[` — push again, start fresh for the innermost part
    - You see `c]` — you've completed `c`, multiply by `2` to get `cc`, pop back to the previous context
    - Now you have `acc`, hit `]` — multiply by `3` to get `accaccacc`

    The stack lets you "pause" your current string-building work, dive into a nested pattern, resolve it, and then resume where you left off.

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

    **Step 1: Initialise data structures**

    - `stack`: Holds pairs of (previous_string, repeat_count) when we enter a new `[`
    - `current_string`: The string we're building at the current nesting level
    - `current_num`: Accumulates multi-digit numbers like `12` or `300`

    &nbsp;

    **Step 2: Iterate through each character**

    - **If digit**: Accumulate into `current_num` (handle multi-digit numbers with `current_num = current_num * 10 + int(char)`)
    - **If `[`**: Push `(current_string, current_num)` onto the stack, then reset both to start fresh for the nested content
    - **If `]`**: Pop from stack, multiply `current_string` by the popped count, and prepend the popped string
    - **If letter**: Simply append to `current_string`

    &nbsp;

    **Step 3: Return the result**

    - After processing all characters, `current_string` contains the fully decoded string

    &nbsp;

    The key insight is that we save our "context" (what we've built so far and how many times to repeat the upcoming section) whenever we enter a new nesting level, then restore and combine when we exit.

  common_pitfalls:
    - title: Forgetting Multi-Digit Numbers
      description: |
        The repeat count `k` can be more than one digit! For example, `12[a]` means repeat `a` twelve times, not `1` followed by `2[a]`.

        When you encounter a digit, you need to accumulate: `current_num = current_num * 10 + int(char)`. This handles numbers like `123` correctly by building up `1` → `12` → `123`.
      wrong_approach: "Treating each digit as a separate number"
      correct_approach: "Accumulate digits: current_num = current_num * 10 + digit"

    - title: Not Resetting State After Opening Bracket
      description: |
        When you push to the stack upon seeing `[`, you must reset `current_string` to empty and `current_num` to `0`. Otherwise, you'll mix content from different nesting levels.

        For `3[a2[c]]`, after pushing at the first `[`, you need a fresh start to build `a` before encountering `2[c]`.
      wrong_approach: "Continuing to append to the same string after ["
      correct_approach: "Reset current_string and current_num after pushing to stack"

    - title: String Concatenation in Wrong Order
      description: |
        When popping from the stack after `]`, the order matters:
        - `prev_string + (current_string * count)` is correct
        - `(current_string * count) + prev_string` is wrong

        The `prev_string` is what came *before* the `k[...]` pattern, so it should stay at the front.
      wrong_approach: "Appending prev_string after the repeated content"
      correct_approach: "Prepend prev_string: result = prev_string + current_string * count"

    - title: Using Recursion Without Tracking Position
      description: |
        A recursive approach is valid but requires careful handling of the current position. You need to return both the decoded string *and* the index where you stopped, so the caller knows where to resume.

        The iterative stack approach avoids this complexity by maintaining state explicitly.

  key_takeaways:
    - "**Stack for nested structures**: Whenever you see nested patterns with matching pairs (brackets, parentheses), think stack"
    - "**Save and restore context**: Push your current state when entering a new level, pop and combine when exiting"
    - "**Multi-digit number handling**: Always accumulate digits with `num = num * 10 + digit` for robustness"
    - "**Pattern recognition**: This stack technique applies to many parsing problems — balanced parentheses, expression evaluation, nested HTML tags"

  time_complexity: "O(n * m) where `n` is the length of the encoded string and `m` is the maximum length of the decoded string. Each character is processed once, but string concatenation may involve copying characters multiple times due to repetition."
  space_complexity: "O(n + m) for the stack storing intermediate strings and the output string. In the worst case of deeply nested patterns, the stack depth approaches `n`."

solutions:
  - approach_name: Stack
    is_optimal: true
    code: |
      def decodeString(s: str) -> str:
          stack = []  # Stores (prev_string, repeat_count) pairs
          current_string = ""
          current_num = 0

          for char in s:
              if char.isdigit():
                  # Accumulate multi-digit numbers
                  current_num = current_num * 10 + int(char)

              elif char == '[':
                  # Save current context and start fresh
                  stack.append((current_string, current_num))
                  current_string = ""
                  current_num = 0

              elif char == ']':
                  # Pop context and apply repetition
                  prev_string, repeat_count = stack.pop()
                  current_string = prev_string + current_string * repeat_count

              else:
                  # Regular letter - just append
                  current_string += char

          return current_string
    explanation: |
      **Time Complexity:** O(n * m) — We process each character once, but string operations may copy characters multiple times based on repeat counts.

      **Space Complexity:** O(n + m) — Stack space for nested levels plus the decoded output string.

      The stack elegantly handles nested patterns by saving context when entering brackets and restoring when exiting. Each `]` triggers a "resolve and combine" operation that builds up the final string from the inside out.

  - approach_name: Recursion
    is_optimal: false
    code: |
      def decodeString(s: str) -> str:
          def decode(index: int) -> tuple[str, int]:
              result = ""
              num = 0

              while index < len(s):
                  char = s[index]

                  if char.isdigit():
                      # Accumulate the repeat count
                      num = num * 10 + int(char)

                  elif char == '[':
                      # Recurse to decode the nested content
                      decoded, index = decode(index + 1)
                      result += decoded * num
                      num = 0

                  elif char == ']':
                      # End of current level - return to caller
                      return result, index

                  else:
                      # Regular letter
                      result += char

                  index += 1

              return result, index

          decoded_string, _ = decode(0)
          return decoded_string
    explanation: |
      **Time Complexity:** O(n * m) — Similar to the stack approach, processing each character with potential string repetition.

      **Space Complexity:** O(n) — Recursion depth proportional to nesting level, plus O(m) for output.

      The recursive approach mirrors the stack solution but uses the call stack implicitly. Each `[` triggers a recursive call, and each `]` returns to the caller. The key is returning both the decoded string *and* the current index so the caller knows where to resume parsing.
