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 times. Note that 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, . 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 .
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 is a positive integer ands
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]
wheres
is an encoded string and is an integer. We can build the answer by concatenating copies ofdecodeString(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 close bracket but open brackets. 5[abc3[ba]]
however, is complete since we have open and close brackets.
Time Complexity
Let represent the length of the final string we return. Since we construct a string of length , our time complexity is .
Time Complexity:
Space Complexity
Since we build a string with length , our space complexity is also .
Space Complexity:
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)
Which algorithm should you use to find a node that is close to the root of the tree?
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
Which algorithm is best for finding the shortest distance between two points in an unweighted graph?
How does quick sort divide the problem into subproblems?
Recommended Readings
Top Patterns to Conquer the Technical Coding Interview Should the written word bore you fear not A delightful video alternative awaits iframe width 560 height 315 src https www youtube com embed LW8Io6IPYHw title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
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.