Decode String

A string has been compressed with the rule k[text], which stands for text written out k times in a row. For example 3[ab] means ababab. The pieces can be nested, as in 2[x2[y]] = xyyxyy, and can be mixed with plain letters, as in q2[r]s = qrrs.

Given a compressed string s, return the fully expanded string.

You may assume s is well formed: brackets are balanced, every [ is directly preceded by a positive integer k (which may have several digits), and digits appear only as repeat counts, never in the original text.

Example 1

Input: s = "3[xy]2[z]" Output: "xyxyxyzz" Why: "xy" three times, followed by "z" twice.

Example 2

Input: s = "2[a3[bc]]" Output: "abcbcbcabcbcbc" Why: The inner 3[bc] expands to "bcbcbc", so each copy of the outer group is "abcbcbc".

Example 3

Input: s = "pre2[mid]post" Output: "premidmidpost" Why: Plain letters outside brackets are copied as they are.

Constraints

  • 1 <= s.length <= 30
  • s consists of lowercase English letters, digits and square brackets
  • 1 <= k <= 300
  • The decoded string has length at most 105.

Pattern Path · Stacks · problem 46 of 169

When you reach a ], you need the text built since the matching [ and the number in front of it. Which data structure gives you the most recent unfinished thing first?
Keep a "current" string being built. On [, set it aside together with the repeat count you just read, and start a fresh current string.
On ], pop the saved (previous string, k) pair and set current = previous + current × k. Remember that a count can span several digits.
Target: O(L) for output length L time, O(L) extra space.

Start simple

Repeatedly find an innermost k[...] with no brackets inside, expand it, and rebuild the string until no brackets remain: O(L·d) time for output length L and nesting depth d, with lots of copying.

The key idea

Nested brackets are processed inside-out, and the innermost open group is always the most recent one, which is exactly what a stack tracks.

Scan the string once while keeping two things: current, the decoded text of the group you are inside, and num, the repeat count being read. Then:

  • a digit: num = num * 10 + digit (counts can have several digits);
  • [: push (current, num), then reset both, since a new group starts;
  • ]: pop (previous, k) and set current = previous + current * k, which closes the group and glues it onto the text that came before it;
  • a letter: append it to current.

When the scan ends, current is the answer. Each saved prefix waits on the stack until its group closes, so nesting of any depth is handled. A recursive descent parser (a function that decodes until it meets a ]) is an equivalent alternative.

Complexity

  • Time: O(L) for output length L
  • Space: O(L)

Python

class Solution:
    def decodeString(self, s: str) -> str:
        stack = []      # (text before the group, repeat count) for each open "["
        current = ""
        num = 0
        for ch in s:
            if ch.isdigit():
                num = num * 10 + int(ch)
            elif ch == "[":
                stack.append((current, num))
                current, num = "", 0
            elif ch == "]":
                previous, k = stack.pop()
                current = previous + current * k
            else:
                current += ch
        return current

JavaScript

var decodeString = function(s) {
  const stack = []; // [text before the group, repeat count] for each open "["
  let current = "";
  let num = 0;
  for (const ch of s) {
    if (ch >= "0" && ch <= "9") {
      num = num * 10 + Number(ch);
    } else if (ch === "[") {
      stack.push([current, num]);
      current = "";
      num = 0;
    } else if (ch === "]") {
      const [previous, k] = stack.pop();
      current = previous + current.repeat(k);
    } else {
      current += ch;
    }
  }
  return current;
};

What the interviewer is checking

This tests parsing with a stack: handling nesting, multi-digit numbers and the "save context, then restore it" step cleanly.

Mistakes to avoid

  • Reading only one digit, so 12[a] becomes "aa" preceded by garbage.
  • Forgetting to reset the count after pushing it on [.
  • Dropping the letters that appear before a bracket group or after the last ].

Follow-ups to expect

  • Write the encoder that produces the shortest string. (Interval DP over substrings.)
  • Return only the character at index i. (Track lengths, avoid expanding.)
  • Solve it recursively. (Parse until the matching bracket.)

In real software

Run-length style expansion appears in template engines, in repeat syntax for sequence and pattern languages, and in any parser that must handle nested groups such as JSON or arithmetic expressions.

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url