Decode String
Problem
Given an encoded string s, return its decoded string where the encoding rule is k[encoded_string], meaning the encoded_string inside the square brackets is repeated exactly k times. You may assume the input string is always valid and does not contain extra white spaces.
- 1 ≤ s.length ≤ 30
- s consists of lowercase English letters, digits, and square brackets '[]'.
- s is guaranteed to be a valid encoding.
- All integers in s are in the range [1, 300].
Example
s = "3[a2[c]]""accaccacc"Example s = "3[a2[c]]" We scan left to right and maintain: - num: the repeat count we’re currently building (handles multi-digit like 12) - cur: the decoded text for the current bracket level, stored as a character list - stack: saved outer contexts as (prev_prefix_list, repeat_count) Start: num = 0, cur = [], stack = [] 1) Read "3" - This is a repeat count. - num = 3 2) Read "[" - We’re entering a bracketed block that will be repeated num times. - Save the current outer context, then start a fresh inner context: - stack.push(([], 3)) - Reset: cur = [], num = 0 3) Read "a" - Regular character, append to current level. - cur = ["a"] 4) Read "2" - Another repeat count for the next bracket. - num = 2 5) Read "[" - Enter a deeper nested block. Save what we have so far at this level. - stack.push((["a"], 2)) - Reset: cur = [], num = 0 6) Read "c" - Append letter. - cur = ["c"] 7) Read "]" (close inner block) - Pop the last saved context: prev = ["a"], k = 2 - Repeat the inner decoded chunk: ["c"] * 2 = ["c", "c"] - Stitch back into the previous prefix: ["a"] + ["c", "c"] = ["a", "c", "c"] - Now cur = ["a", "c", "c"] (we’re back inside the outer 3[...]) 8) Read "]" (close outer block) - Pop next context: prev = [], k = 3 - Repeat current chunk: ["a", "c", "c"] * 3 = ["a", "c", "c", "a", "c", "c", "a", "c", "c"] - Stitch back: [] + that = same list - Now cur = ["a", "c", "c", "a", "c", "c", "a", "c", "c"] End: - return ''.join(cur) -> "accaccacc"
Approach
Straightforward Solution
A naive approach might try to parse and decode the string recursively or with repeated string replacements, but this quickly becomes complex and inefficient, especially with nested patterns. Without a stack, managing nested contexts and repeat counts is error-prone.
Core Observation
The problem requires decoding nested repeated substrings, where each repeat count applies to the substring immediately following it inside brackets. The nested structure implies a last-in-first-out (LIFO) context management, naturally modeled by a stack.
Path to Optimal
PreviewRecognizing the nested, hierarchical structure of the encoding suggests using a stack to store the current prefix and repeat count before entering a new bracketed substring. Each time a '[' is encountered, the current state is pushed onto the stack, and a new context begins…
Full step-by-step walkthrough on Pro →
Optimal Approach
PreviewIterate through the string character by character. Maintain a current prefix (as a list of characters) and a current number accumulator…
Full step-by-step walkthrough on Pro →
Want the full reasoning chain?
Unlock the complete walkthrough, line-by-line analysis, and recall drill.
Unlock ProTime
O(n + m)
Let n be the length of the input string and m be the length of the decoded output. The algorithm scans the input once (O(n)). Each closing bracket expansion must materialize decoded characters; across all expansions the total number of characters produced is m, so the total work for expansions is O(m). Therefore overall time is O(n + m).
Space
O(m + d)
Let m be the decoded output length and d be the maximum nesting depth. The output requires O(m) space. The stack stores up to d frames (previous prefix plus repeat count). Total space is O(m + d).
Pattern Spotlight
Stack (State Preservation for Nested Structures)
When decoding nested or hierarchical structures with matching delimiters, use a stack to save and restore context at each level, enabling clean, linear-time parsing of arbitrarily nested patterns.
Solution
| 1 | class Solution: |
| 2 | def decodeString(self, s: str) -> str: |
| 3 | stack = [] |
| 4 | cur = [] |
| 5 | num = 0 |
| 6 | |
| 7 | for c in s: |
| 8 | if c.isdigit(): |
| 9 | num = num * 10 + int(c) |
| 10 | elif c == '[': |
| 11 | stack.append((cur, num)) |
| 12 | cur = [] |
| 13 | num = 0 |
| 14 | elif c == ']': |
| 15 | prev, k = stack.pop() |
| 16 | cur = prev + cur * k |
| 17 | else: |
| 18 | cur.append(c) |
| 19 | |
| 20 | return ''.join(cur) |
Step-by-Step Solution
Initialize Stack and Current State Variables for Decoding
| 3 | stack = [] |
| 4 | cur = [] |
| 5 | num = 0 |
Objective
To set up the data structures and variables that track the decoding state as the string is processed.
Key Insight
The stack holds tuples of (previous_prefix, repeat_count) to preserve context when entering nested brackets. The current prefix is built as a list of characters for efficient concatenation, and the current number accumulates multi-digit repeat counts. This setup enables seamless transitions between nested levels.
Interview Quick-Check
Core Logic
Using a stack to store previous prefixes and repeat counts preserves the decoding context for nested brackets.
State & Boundaries
Initialize current prefix as a list and number as zero to handle incremental building and multi-digit numbers.
Common Pitfalls & Bugs
Failing to reset the current prefix and number after pushing onto the stack leads to incorrect concatenations.
Iterate Through Input String to Parse Digits, Brackets, and Characters
To process each character, updating the current number, pushing/popping stack states, and building the decoded string accordingly.
Return the Fully Decoded String by Joining Current Characters
To produce the final decoded string by concatenating all characters accumulated in the current prefix list.
2 more steps with full analysis available on Pro.
Line Analysis
This solution has 4 Critical lines interviewers watch for.
cur = prev + cur * k
Update the current prefix by concatenating the previous prefix with the current decoded substring repeated k times.
This concatenation embodies the core decoding logic, combining the repeated substring with the previously decoded prefix, enabling correct reconstruction of nested encoded patterns.
num = num * 10 + int(c)
Update the current number by shifting previous digits and adding the new digit.
This line implements the critical logic for parsing multi-digit repeat counts, ensuring that numbers like '12' are correctly interpreted as twelve rather than separate digits.
stack.append((cur, num))
Push the current prefix and number onto the stack to save context.
This push operation is the core mechanism that preserves the decoding context for nested brackets, allowing the algorithm to correctly resume previous states after decoding inner substrings.
Full line-by-line criticality + rationale for all 16 lines available on Pro.
Test Your Understanding
Why is a stack necessary to correctly decode nested encoded strings instead of using simple string concatenation or recursion without explicit state management?
See the answer with Pro.
Related Problems
Stacks pattern
Don't just read it. Drill it.
Reconstruct Decode String from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Decode String drill