0

The prompt: Given an encoded string, return its corresponding decoded string. The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is repeated exactly k times. Note: k is guaranteed to be a positive integer.

For s = "4[ab]", the output should be decodeString(s) = "abababab" For s = "2[b3[a]]", the output should be decodeString(s) = "baaabaaa"

My answer:

let decodeString = function(s) {

  const replaced = s.replace((/(\d+)\[([a-z]*)\]/), (match, num, substr) => {
    return substr.repeat(num);
  })

  return replaced.indexOf('[') > -1 ? decodeString(replaced) : replaced; 

};

This solution works for smaller inputs like "3[a]2[bc]" but I get stack overflow for more complex inputs like "3[a]2[b4[F]c]". Is there a way to do this iteratively to avoid using the call stack? Is it possible to solve this question by only using regex? Any help is much appreciated!

Alec Davidson
  • 175
  • 1
  • 3
  • 12

3 Answers3

2

function decodeString(str){
 let regex = /(\d+)\[([a-z]*)\]/gi;
 let result = str.replace(regex, (match, num, substr) => substr.repeat(num));
 let recall = regex.test(result);
 if(recall){
  return decodeString(result);
 }
 return result;
}

console.log(
  decodeString("4[ab]"),
  decodeString("2[b3[a]]"),
  decodeString("3[a]2[b4[F]c]")
)
0

You can just replace your recursive call with a while loop, based on the same condition:

function decodeString(s) {
    while (s.includes("[")) {
        s = s.replace(/(\d+)\[([a-z]*)\]/, (match, num, substr) => substr.repeat(num));
    }
    return s;
}

console.log(decodeString("2[b3[a]]"));
trincot
  • 317,000
  • 35
  • 244
  • 286
  • Yes. Thank you for this. This is the iterative solution I was looking for. The stack overflow was actually because of a missing i flag in the regex – Alec Davidson Jun 11 '18 at 18:27
  • Indeed, if you have capitals or more opening than closing braces, or missing digits before them, or characters that do not match with any of those, ... it will not stop. If the input is not guaranteed to match with the pattern, it would be better to compare the string before and after the replacement: if nothing changed: exit; – trincot Jun 11 '18 at 19:02
0

It is interesting to see that a few elegant lines of regex code will reproduce the result that the approach of iterating through the string requires 60 or so lines of code to accomplish. However, I thought it would also be interesting to benchmark some of the various implementations I've encountered.

I find that the purely iterative approach, with recursive calls for nested brackets, is significantly faster than the other implementations. Here are the results:

  1. Iterate string, call procedure recursively for nested brackets: 136,943 ops/sec
  2. Iterate string without using recursive procedure: 85,775 ops/sec
  3. Luis Felipe's regex: 81,197 ops/sec
  4. trincot's regex, replacing recursive call with while loop: 80,784 ops/sec

Of course, there are other variables between 1 and 2 that could be significantly slowing down the latter. But on first look the hypothesis that recursion improves performance in this case appears reasonable.

Here's the jsperf.

Implementation 1 is here. Implementation 2 is here. Interestingly, implementation 1 is the only one that runs fast enough to pass the CodeSignal tests.

BobRodes
  • 5,990
  • 2
  • 24
  • 26