questions D-E

This commit is contained in:
2025-05-25 11:08:40 +01:00
parent 615e3f1291
commit 360b5fa255
18 changed files with 4022 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
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.