How to Solve the “Decode a String” Challenge in Java

2 min read 479 words

Table of Contents

The challenge

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 k times. Note that k is guaranteed to be a positive integer.

You may assume that the input string is always valid; 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, k. For example, there won’t be input like 3a or 2[4].

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"

Example 4:

Input: s = "abc3[cd]xyz"
Output: "abccdcdcdxyz"

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

Let’s start by writing some Test cases

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class DecodeStringTest {

    DecodeString ds = new DecodeString();

    @Test
    void decodeString1() {
        String actual = ds.decodeString("3[a]2[bc]");
        String expected = "aaabcbc";
        assertEquals(expected, actual);
    }
    @Test
    void decodeString2() {
        String actual = ds.decodeString("3[a2[c]]");
        String expected = "accaccacc";
        assertEquals(expected, actual);
    }
    @Test
    void decodeString3() {
        String actual = ds.decodeString("2[abc]3[cd]ef");
        String expected = "abcabccdcdcdef";
        assertEquals(expected, actual);
    }
    @Test
    void decodeString4() {
        String actual = ds.decodeString("abc3[cd]xyz");
        String expected = "abccdcdcdxyz";
        assertEquals(expected, actual);
    }
}

This is all pretty simple, nothing onerous here so far.

The solution in Java code

At first we could try and knock out something like the following:

public class DecodeString {
    public String decodeString(String s) {

        StringBuilder sb = new StringBuilder();

        for (int i=0; i<s.length(); i++) {
            if (Character.isDigit(s.charAt(i))) {
                int timesToRepeat = Integer.parseInt(""+s.charAt(i));

                StringBuilder repeats = new StringBuilder();
                for (int j=++i; j<s.length(); j++) {
                    if (s.charAt(j)=='[') {
                        //start of group
                    } else if (s.charAt(j)==']') {
                        //end of group
                        break;
                    } else {
                        //has repeat character
                        repeats.append(s.charAt(j));
                    }
                    i++;
                }
                sb.append(repeats.toString().repeat(timesToRepeat));

            } else {
                sb.append(s.charAt(i));
            }
        }


        return sb.toString();
    }
}

BUT! When we run this against our test cases, we’ll notice the following:

org.opentest4j.AssertionFailedError: 
Expected :accaccacc
Actual   :a2ca2ca2c]

This is because test-case number 2 makes our lives a little harder by requiring a wrapped expression:

So attempt number 2 at this solution would be to approach the problem from the perspective of a stack:

import java.util.Stack;

public class DecodeString {
    public String decodeString(String s) {
        Stack<Integer> freqStack = new Stack<>();
        Stack<StringBuilder> strStack = new Stack<>();

        StringBuilder currStr = new StringBuilder();
        int k =0;
        for(char c :  s.toCharArray()){
            if(Character.isDigit(c)){
                k = k*10 + (c-'0');
            } else if (Character.isLetter(c)){
                currStr.append(c);
            } else if(c == '['){
                freqStack.push(k);
                strStack.push(currStr);
                k =0;
                currStr = new StringBuilder();
            } else if(c == ']'){
                StringBuilder temp = currStr;
                int freq = freqStack.pop();
                currStr = strStack.pop();
                while(freq-->0){
                    currStr.append(temp);

                }
                k = 0;
            }
        }
        return currStr.toString();
    }
}
Tags:
Andrew
Andrew

Andrew is a visionary software engineer and DevOps expert with a proven track record of delivering cutting-edge solutions that drive innovation at Ataiva.com. As a leader on numerous high-profile projects, Andrew brings his exceptional technical expertise and collaborative leadership skills to the table, fostering a culture of agility and excellence within the team. With a passion for architecting scalable systems, automating workflows, and empowering teams, Andrew is a sought-after authority in the field of software development and DevOps.

Tags