LeetCode #394 Medium

Decode String

Decode String is LeetCode 394 (Medium). Given an encoded string s, return its decoded form. The rule is k[encoded_string]: the text inside the brackets is repeated exactly k times.

  • Groups can be nested, so an inner group must be expanded before its outer count applies.
  • k is a positive integer up to 300, so it can have several digits.
  • The input is always valid, and the original text never contains digits.
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].
  • The output length never exceeds 10⁵.
stringstackrecursion
Open on LeetCode ↗
02

Intuition

When a [ opens, the outer group is only half built: its count and the text before the bracket are known, but its inside is not. So the outer group has to pause while the inner one is finished, and then carry on.

The group opened last always closes first, which is exactly what a stack handles. Save the outer count and text at each [, and at the matching ] take them back and join the pieces. One left-to-right pass decodes any depth of nesting.

How to spot this pattern

Nested, balanced brackets where the inside must be finished before the outside can use it call for a stack that saves the outer context at each opener and restores it at the closer. Decode String shares this shape with Basic Calculator (parentheses with a saved sign and result) and Valid Parentheses.

03

Approach

Try it first

Before reading on: in 3[a2[c]], which group must be finished first? While you work on it, what two facts about the outer group must you remember so you can finish that one afterwards?

1

Fold digits into the count

On a digit, set num = num * 10 + digit. A count like 12 arrives as two characters, so it has to be built up digit by digit and used only when the [ arrives.

2

Save the outer level at [

Push num and current (the text built so far at this level), then reset both. The count says how often the coming group repeats; the saved text must end up in front of it.

3

Rebuild at ]

Pop the count k and the saved text, and set current = saved + current * k. The saved text goes first because it was written before the bracket. The result is now ordinary text of the level one out.

4

Append letters, then return

A letter just extends current. After the last character every bracket has been closed, so the stack is empty and current is the decoded string. The work is proportional to the length of the output.

04

Decode String solution in Python | C++ | Java

▶1class Solution:
▶2 def decodeString(self, s: str) -> str:
▶3 count_stack, string_stack = [], []
▶4 current, num = "", 0
▶5 for ch in s:
▶6 if ch.isdigit():
▶7 num = num * 10 + int(ch)
▶8 elif ch == "[":
▶9 count_stack.append(num)
▶10 string_stack.append(current)
▶11 current, num = "", 0
▶12 elif ch == "]":
▶13 current = string_stack.pop() + current * count_stack.pop()
▶14 else:
▶15 current += ch
▶16 return current
s3[a]2[bc]num0currentemptystackemptycurrent empty, num 0, stack empty
currentempty
num0
stacknone
Start. current is the text of the group being built and num the count read so far. The stack will save the outer level each time a group opens, because an inner group must be finished before its outer count can apply.
s3[a]2[bc]num3currentemptystackemptydigit 3 → num = 0 × 10 + 3 = 3
char3
num3
A digit is folded into num as num × 10 + 3, not stored on its own: a count like 12 arrives as two characters, and it is only used once the [ shows up.
s3[a]2[bc]num0currentemptystackktext before [3empty← top[ → save 3 and empty, reset
pushed3 and empty
currentemptyinner group
num0
stack1 saved
A group opens. Save two things: the count 3 that will repeat this group, and the text built before it, which is nothing yet. That text must end up in front of the group. Then reset both, so the inner group starts from nothing.
s3[a]2[bc]num0currentastackktext before [3empty← topletter a → current = "a"
chara
current"a"
A letter just extends current, the text of the innermost open group. The stack is not touched until a bracket appears.
s3[a]2[bc]num0currentaaastackemptyrebuildempty+a×3=aaa] → empty + "a" × 3
popped3 and empty
current"aaa"
stacknone
The group closes. Pop the top level: repeat "a" 3 times and put the saved text, empty here, in front, since it came before the [. The result becomes current again, so the outer text keeps growing from here.
s3[a]2[bc]num2currentaaastackemptydigit 2 → num = 0 × 10 + 2 = 2
char2
num2
A digit is folded into num as num × 10 + 2, not stored on its own: a count like 12 arrives as two characters, and it is only used once the [ shows up.
s3[a]2[bc]num0currentemptystackktext before [2aaa← top[ → save 2 and "aaa", reset
pushed2 and "aaa"
currentemptyinner group
num0
stack1 saved
A group opens. Save two things: the count 2 that will repeat this group, and the text built before it ("aaa"). That text must end up in front of the group. Then reset both, so the inner group starts from nothing.
s3[a]2[bc]num0currentbstackktext before [2aaa← topletter b → current = "b"
charb
current"b"
A letter just extends current, the text of the innermost open group. The stack is not touched until a bracket appears.
s3[a]2[bc]num0currentbcstackktext before [2aaa← topletter c → current = "bc"
charc
current"bc"
A letter just extends current, the text of the innermost open group. The stack is not touched until a bracket appears.
s3[a]2[bc]num0currentaaabcbcstackemptyrebuildaaa+bc×2=aaabcbc] → "aaa" + "bc" × 2
popped2 and "aaa"
current"aaabcbc"
stacknone
The group closes. Pop the top level: repeat "bc" 2 times and put the saved text "aaa" in front, since it came before the [. The result becomes current again, so the outer text keeps growing from here.
s3[a]2[bc]num0currentaaabcbcstackemptyreturn "aaabcbc"
answer"aaabcbc"
Decoded: "aaabcbc". Every [ saved a level and its ] restored it, so the stack is empty again and current holds the whole string after one pass.
05

Recursive decode

decode() reads from a shared index i. Letters are appended, digits build the count, a [ calls decode() for the inner group and repeats its result, and a ] returns the group built so far.

▶1class Solution:
▶2 def decodeString(self, s: str) -> str:
▶3 i = 0
▶4 
▶5 def decode() -> str:
▶6 nonlocal i
▶7 out, num = [], 0
▶8 while i < len(s):
▶9 ch = s[i]
▶10 i += 1
▶11 if ch.isdigit():
▶12 num = num * 10 + int(ch)
▶13 elif ch == "[":
▶14 out.append(decode() * num)
▶15 num = 0
▶16 elif ch == "]":
▶17 break
▶18 else:
▶19 out.append(ch)
▶20 return "".join(out)
▶21 
▶22 return decode()
06

Common pitfalls

Reading one digit as the whole count

✗ Wrong
num = int(ch)
✓ Right
num = num * 10 + int(ch)

Each digit overwrites the last, so 12[a] keeps only the 2 and returns two copies instead of twelve.

Not resetting num after [

✗ Wrong
count_stack.append(num)
string_stack.append(current)
current = ""
✓ Right
count_stack.append(num)
string_stack.append(current)
current, num = "", 0

The old count is still in num, so the next digit is added on top of it: in 3[2[a]] the inner count becomes 32 instead of 2.

Putting the saved text after the group

✗ Wrong
current = current * count_stack.pop() + string_stack.pop()
✓ Right
current = string_stack.pop() + current * count_stack.pop()

The saved text was written before the [, so it goes in front. Reversed, a2[b] decodes to bba instead of abb.

07

Edge cases

Letters after the last ], 2[a]bc

They extend current at the outer level, giving aabc.

08

Complexity

Time
O(n)
Space
O(n)
n is the length of the decoded output, which is at most 10⁵ and can be much longer than s. Each output character is written a constant number of times per enclosing level.
09

Decode String FAQ

Can the Python solution use one stack?

Yes. Keep one stack of [text, count] pairs: on [ push the pair and reset, on ] pop it and set current = text + current * count. It is the same algorithm with the two stacks zipped together; Python's string repetition current * count does the repeating.