394. Decode String

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 kk times. Note that kk 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, kk. 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 10510^5.

Example 1:

Input: s = "3[a]2[bc]"
Output: "aaabcbc"

Example 2:

Input: s = "3[a2[c]]"
Output: "accaccacc"

Example 3:

Input: s = "2[abc]3[cd]ef"
Output: "abcabccdcdcdef"

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].

Solution

Solution

Here is the definition of an encoded string:

  • a string is encoded if it only consists of lowercase English letters
  • a string is encoded if it's in the form k[s] where kk is a positive integer and s is a encoded string
  • a string is encoded if it's a concatenation of two encoded strings

We can notice that an encoded string can be a concatenation of multiple encoded strings. To decode this string, we can separate the string into the multiple encoded strings, decode them separately and finally concatenate all the decoded strings together.

Here, we're solving a problem that can be broken down into the same repetitive problem. Thus, we can use recursion. The function decodeString() will return the decoded string of any encoded string.

There's two basic cases we should consider:

  • The string is consists of only lowercase English letters. In this case, we can just return the original string.
  • a string in the form k[s] where s is an encoded string and kk is an integer. We can build the answer by concatenating kk copies of decodeString(s).

Any encoded string is just a concatenation of these cases. For any string, we'll first break it down into a bunch of strings that follow one of the two basic cases. Then we'll decode those separately and concatenate them together in the end.

For example, the string 5[abc3[ba]]jkl4[xyz] can be separated into 5[abc3[ba]], jkl, and 4[xyz]. We can find the decoded strings separately by using the function decodeString() and then we'll concatenate them together.

For the basic case in the form k[s], how will we find the matching close bracket? When iterating through the string, we know we have found the correct close bracket when we reach a point in the string where the number of open brackets match the number of close brackets. In the same example, 5[abc3[ba] is currently incomplete since we only have 11 close bracket but 22 open brackets. 5[abc3[ba]] however, is complete since we have 22 open and close brackets.

Time Complexity

Let LL represent the length of the final string we return. Since we construct a string of length LL, our time complexity is O(L)\mathcal{O}(L).

Time Complexity: O(L)\mathcal{O}(L)

Space Complexity

Since we build a string with length LL, our space complexity is also O(L)\mathcal{O}(L).

Space Complexity: O(L)\mathcal{O}(L)

C++ Solution

1class Solution {
2    int stringToInteger(string s) {
3        int ans = 0;
4        for (char nxt : s) {
5            ans *= 10;
6            ans += nxt - '0';
7        }
8        return ans;
9    }
10
11   public:
12    string decodeString(string s) {
13        string ans = "";
14        int prev = 0;
15        int repetitions = 0;
16        int depth = 0;  // keeps track of # open bracket - # close bracket
17        for (int i = 0; i < s.size(); i++) {
18            if (depth == 0 && 'a' <= s[i] && s[i] <= 'z') {
19            	  // case with lowercase letters
20                ans.push_back(s[i]);
21                prev = i + 1;
22            }
23            if (s[i] == '[') {
24                depth++;
25                if (depth == 1) {  // open bracket for the case "k[s]"
26                    repetitions = stringToInteger(s.substr(prev, i - prev));
27                    prev = i + 1;
28                }
29            } else if (s[i] == ']') {
30                depth--;
31                if (depth == 0) {              // close bracket for the case "k[s]"
32                    while (repetitions > 0) {  // add k copies of s
33                        ans += decodeString(s.substr(prev, i - prev));
34                        repetitions--;
35                    }
36                    prev = i + 1;
37                }
38            }
39        }
40        return ans;
41    }
42};

Java Solution

1class Solution {
2    private int stringToInteger(String s) {
3        int ans = 0;
4        for (int i = 0; i < s.length(); i++) {
5            ans *= 10;
6            ans += s.charAt(i) - '0';
7        }
8        return ans;
9    }
10    public String decodeString(String s) {
11        StringBuilder ans = new StringBuilder();
12        int prev = 0;
13        int repetitions = 0;
14        int depth = 0; // keeps track of # open bracket - # close bracket
15        for (int i = 0; i < s.length(); i++) {
16            if (depth == 0 && 'a' <= s.charAt(i) && s.charAt(i) <= 'z') {
17            	  // case with lowercase letters
18                ans.append(s.charAt(i));
19                prev = i + 1;
20            }
21            if (s.charAt(i) == '[') {
22                depth++;
23                if (depth == 1) { // open bracket for the case "k[s]"
24                    repetitions = stringToInteger(s.substring(prev, i));
25                    prev = i + 1;
26                }
27            } else if (s.charAt(i) == ']') {
28                depth--;
29                if (depth == 0) { // close bracket for the case "k[s]"
30                    while (repetitions > 0) { // add k copies of s
31                        ans.append(decodeString(s.substring(prev, i)));
32                        repetitions--;
33                    }
34                    prev = i + 1;
35                }
36            }
37        }
38        return ans.toString();
39    }
40}

Python Solution

Note: The same recursion here will be done with the function decode().

1class Solution:
2    def decodeString(self, s: str) -> str:
3        def stringToInteger(s):
4            ans = 0
5            for ch in s:
6                ans *= 10
7                ans += int(ch) - int("0")
8            return ans
9
10        def decode(s):
11            ans = str()
12            prev = 0
13            repetitions = 0
14            depth = 0  # keeps track of # open bracket - # close bracket
15            for i in range(len(s)):
16                if (depth == 0 and "a" <= s[i] and s[i] <= "z"):
17                	  # case with lowercase letters
18                    ans += s[i]
19                    prev = i + 1
20                if s[i] == "[":
21                    depth += 1
22                    if depth == 1:  # open bracket for the case "k[s]"
23                        repetitions = stringToInteger(s[prev:i])
24                        prev = i + 1
25                elif s[i] == "]":
26                    depth -= 1
27                    if depth == 0:  # close bracket for the case "k[s]"
28                        while repetitions > 0:  # add k copies of s
29                            ans += decode(s[prev:i])
30                            repetitions -= 1
31                        prev = i + 1
32            return ans
33
34        return decode(s)
Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

Which algorithm should you use to find a node that is close to the root of the tree?

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Solution Implementation

Not Sure What to Study? Take the 2-min Quiz:

Which algorithm is best for finding the shortest distance between two points in an unweighted graph?

Fast Track Your Learning with Our Quick Skills Quiz:

How does quick sort divide the problem into subproblems?


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫