-1

I got this problem in Amazon interview.

Given have a String in Java as input 3[a]2[bc] write a function to decode it so the output should be as "**aaabcbc**"

Input 3[a]2[bc] -> aaabcbc
Input 3[2[a]]4[b] -> aaaaabbbb
Invalid Input 3[4] `enter code here`
Invalid Input a[3]

I have tried the following approach but is not correct as it doesn't address nested elements

String test = "3[a]2[b]5[b]";
Map<Character, Integer> map = new HashMap<>();

char[] characters = test.toCharArray();
for (int i = 0; i < characters.length-1; i++) {
    if(characters[i]=='['){
        if(map.containsKey(characters[i+1])){
            int count = map.get(characters[i+1]);
            map.put(characters[i+1], Character.getNumericValue(characters[i-1])+count);
        }else{
            map.put(characters[i+1], Character.getNumericValue(characters[i-1]));
        }

    }
 }
 for (Map.Entry<Character, Integer> c : map.entrySet()) {
    for (int i = 0; i < c.getValue(); i++) {
        System.out.printf("%s",c.getKey());
    }
}   

What is the correct solution to this?

is it possible to use encapsulation class to decode this problem, if you observe the problem carefully its in the format, can we convert this to object of decoder class. 2[...]3[...]4[...]

class Decoder{ private int count;// digit example 2[a]3[bc]4[d] the count value will be 2,3,4 private String data; // example a,bc,d private Decoder decoder; // for nested values example 3[2[a]] in this case decoder will be 2[a] }

Rahul Mahajan
  • 37
  • 1
  • 7
  • Any attempt made so far? why don't you show us? – Juan Carlos Mendoza Oct 20 '17 at 20:51
  • 1
    @RahulMahajan: Why is `3[2[a]]` expected to be `aaaaa`? Shouldn't it be `3[2[a]]` -> `3[aa]` -> `aaaaaa` ? – Cratylus Oct 20 '17 at 20:55
  • Help you how? Do you need advice on what IDE to use? Are you having problems reading user input? Do you lack an algorithm for "decoding"? Are you having some sort of compile error? Do you have a compiling program that is getting some sort of runtime error? – azurefrog Oct 20 '17 at 20:55
  • 6
    Lacking any specifics, your question in its current form reads simply as "Do my work for me". – azurefrog Oct 20 '17 at 20:56
  • what have you attempted so far Rahul? Maybe if we can see what you have done, we could help you with wherever you are stuck at! – whiplash Oct 20 '17 at 20:57
  • I got this problem in Amazon interview, they gave me an string and asked me to write code to return the response. I used HashMap but not able to fix the nexted [] problem – Rahul Mahajan Oct 20 '17 at 21:19
  • @RahulMahajan:Write what you attempted in order to get help. Now the post is not helpful as we don't know what is the part that blocks you. For all we know you might not even know java at all – Cratylus Oct 20 '17 at 21:23
  • @Cratylus I have mentioned my attempt, its not correct as its fail for multiple scenario. – Rahul Mahajan Oct 20 '17 at 21:28
  • @RahulMahajan Can you please let us know how many a should come in your 2nd example – shivam Oct 20 '17 at 21:36
  • @RahulMahajan What need to be done in case of 4th case , do we need to remove such string from output. – shivam Oct 20 '17 at 22:16
  • @Rahul In 3rd Example , will user have to provide input as integer is there inside of square bracket ? and what need to be done with that input. As of now i am taking input from user and decoding it normally – shivam Oct 20 '17 at 22:21
  • I updated the post including some parts mentioned in the comments like that it is an interview question – Cratylus Oct 21 '17 at 21:03
  • @shivam "a" will decoded to 5 times also the program should validate for invalid input. – Rahul Mahajan Oct 22 '17 at 16:30
  • @rahul Mahajan I have provided you basic structure. Please modify it according to your need – shivam Oct 22 '17 at 18:20
  • @rahul Mahajan, we are not here to provide you exact code – shivam Oct 22 '17 at 18:21
  • @RahulMahajan: Check my answer. It solves the input you mention. If there cases that it does not return what is expected please add them as a comment – Cratylus Oct 22 '17 at 19:12
  • @RahulMahajan: Did you check the answers? Did you test any? You have not provided any feedback on any answer – Cratylus Oct 27 '17 at 16:58
  • @Cratylus I saw your answer, nice approach but its failing for multiple scenario, I have tried it using recursion, take a look and let me know your input on the same – Rahul Mahajan Nov 06 '17 at 20:01
  • @RahulMahajan: Can you give a couple of example inputs that are failing? – Cratylus Nov 13 '17 at 19:14
  • input: 3[2[xyz]]2[b] – Rahul Mahajan Nov 17 '17 at 21:50

5 Answers5

2

Reducing nested expressions like 2[2[a]3[b]] to aabbbaabbb could be done by innermost redux (=reducable expression).

Hence keep substituting the unnested form digit[letters] till nothing more can be reduced.

As this seems homework just a sketch:

String expression = "...";
for (;;) {
    boolean reduced = false;
    for (int i = 0; i < expression.length(); ++i) {
        if (found reducable expression) {
            reduced = true;
            expression = reduced expression;
            break; // Otherwise we would need to correct i.
        }
    }
    if (!reduced) {
        break;
    }
}
  1. 2[2[a]3[b]]
  2. 2[aa3[b]]
  3. 2[aabbb]
  4. aabbbaabbb

A concrete solution, based on pattern matching.

String expression = "...";
Pattern reduxPattern = Pattern.compile("(\\d+)\\[(\\pL*)\\]");
boolean reducing;
do {
    Matcher m = reduxPattern.matcher(expression);
    reducing = false;
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        reducing = true;
        int n = Integer.parseInt(m.group(1));
        String letters = m.group(2);
        String repetition = String.join("", Collections.nCopies(n, letters));
        sb.appendReplacement(repetition);
    }
    m.appendTail(sb);
    expression = sb.toString();
} while (reducing);

As discussed in the comments a stack based solution is superior, though I find it a bit more work.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • I think I solved this with a stack (please check my answer) but I would be interested with how to approach it with a regex – Cratylus Oct 21 '17 at 07:56
  • @Cratylus the stack based solution is **faster**, but more complex for a beginner. Also the error handling is more difficult (ill formed input). Nice to see you provided such an answer +1 – Joop Eggen Oct 21 '17 at 14:15
  • Your answer is based on regex right? I find that a lot more complex as it requires strong regex skills. And the original poster can't be a beginner. He said that was his interview question in Amazon – Cratylus Oct 21 '17 at 20:58
  • @Cratylus: Amazon does hire people right out of college, and people with only a few years of experience. Plenty of beginners there. I can pretty much guarantee that they're not looking for a regular expression solution. – Jim Mischel Oct 22 '17 at 05:00
  • @Cratylus either regex `([0-9])\\[([a-z]*)\\]` or a loop. With a stack there is need for some care. Both are easy, but the stack code would have taken a bit more time for me (even though parsing is my favourite subject). That this was an interview question eluded me when commenting. – Joop Eggen Oct 22 '17 at 18:26
  • @JoopEggen I have tried this problem using recursion, let me know your input. – Rahul Mahajan Nov 06 '17 at 20:14
2

Consider what happens if you add a few operators. That is, "3[a]2[bc]"becomes 3*[a] + 2*[bc]. If you redefine the * operator to mean "repeat," and the + operator to mean "concatenate".

Using the Shunting yard algorithm, you can parse the string into postfix form: 3 a * 2 bc +. Shunting yard easily handles nested expressions. For example, your 3[2[a]]4[b] becomes 3 2 a * * 4 b * +. The nice thing about postfix is that it's very simple to evaluate.

Once you're convinced that the postfix form is being generated correctly, you can either write code to evaluate the postfix expression (which is very easy), or you can modify your shunting yard algorithm to evaluate during the output phase. That is, rather than outputting operands and operators to a string, you push operands onto a stack, and whenever you would have output an operator, you instead pop the operands from the stack, apply the operator, and push the result onto the stack. So your output step becomes:

if (token is an operand)
    push token onto stack
else
    pop operand2
    pop operand1
    result = operand1 <operator> operand2
    push result onto stack

When you're done parsing, there should be one operand on the stack, and you can output that.

An alternative to the postfix approach is to create a binary expression tree, and then evaluate it. Another option is to write a recursive descent parser, although unless you've been working with expression parsing recently, you'll probably have a tough time deriving that during the interview.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
1

Can be a bit cleaner but seems to solve the question mentioned
Update I have updated code to address the issues pointed out by @JimMichel This takes into account multiple digits for number and does not accept malformed input.

public static String decode(String in) {      
    Deque<Character> stack = new ArrayDeque<Character>();
    Deque<Integer> occurencesStack = new ArrayDeque<Integer>();
    StringBuilder result = new StringBuilder();
    int brackets = 0;
    for(int i = 0; i < in.length(); ++i) {
        Character ch = in.charAt(i);
        if(ch == '[') {
            ++brackets;
            continue;
        }
        else if(ch == ']') {
            --brackets;
            StringBuilder temp = new StringBuilder();               
            while(!stack.isEmpty()) {
                Character top = stack.pop();
                temp.append(top);               
            }
            int times = occurencesStack.pop();
            if(temp.length() == 0) {
                temp = new StringBuilder(result);
                result.setLength(0);
                for(int j = 0; j < times; ++j) {
                    result.append(temp);
                }                   
            }
            else {
                temp.reverse();
                for(int j = 0; j < times; ++j) {
                    result.append(temp);
                }
                temp.setLength(0);              
            }
        }
        else if(Character.isDigit(ch)) {                
            StringBuilder nb = new StringBuilder();
            nb.append(ch);
            while(i < in.length() - 1 && Character.isDigit(in.charAt(i + 1))) {
                nb.append(in.charAt(i + 1));
                ++i;                    
            }
            if(i < in.length() - 1 && in.charAt(i + 1) == ']') {
                throw new IllegalArgumentException("Invalid sequence");
            }
            occurencesStack.push(Integer.parseInt(nb.toString()));
        }
        else if(ch >= 'a' && ch <= 'z') {   
            if(i < in.length() - 1 && in.charAt(i + 1) == '[') {
                throw new IllegalArgumentException("Invalid sequence");
            }
            stack.push(ch);

        }
        else {
            throw new IllegalArgumentException("Invalid character in sequence "+ch);
        }           
    }

    if(brackets != 0) {
        throw new IllegalArgumentException("Unmatched brackets!");
    }

  return result.toString();  

}    
Cratylus
  • 52,998
  • 69
  • 209
  • 339
  • Interesting approach. You'll want to handle mal-formed input like `a[4]` or `4[3[a]` (unbalanced brackets), or `4]a[`, etc. I would also recommend that you not assume that the number will be a single digit. – Jim Mischel Oct 22 '17 at 05:27
  • @JimMischel: Very good comments. I updated the code to include multiple digits for numbers and malformed input. Seems correct but I am looking forward to your review comments if you time to give me some – Cratylus Oct 22 '17 at 17:18
0

Assumptions made as question is having ambiguity :

For 2nd Case ,3[2[a]] -- > 3[aa]-- > aaaaaa

For 3rd Case ,If integer is there inside of square bracket –user will provide input and decoding it usually.

For 4th case ,If integer is there before outside of square bracket –removing such string from output.

Can you please try this code. I asked some queries in comments please clarify them also.

import java.util.Random;
import java.util.Scanner;


public class MyClass {
    private static String code = "3[a]2[bc]";

    private static class Pair {
        int s = 0;
        int e = 0;
    }


    private static Pair getPair() {
        char[] chars = code.toCharArray();
        Pair pair = new MyClass.Pair();
        int pointer = 0;
        for (char c : chars) {
            if (c == '[') {
                pair.s = pointer;
            }
            if (c == ']') {
                pair.e = pointer;
                break;
            }
            pointer = pointer + 1;
        }
        if (pair.e > (pair.s + 1) ||  pair.s !=0) {
            return pair;
        }else{
            return null;
        }

    }

    private static boolean parseInteger(String s)
    {
        try {
            Integer.parseInt(s);
            return true;
        } catch(NumberFormatException e) {
            return false;
        }
    }

    private static void decode(Pair pair){
        String pattern = code.substring(pair.s+1, pair.e);
        String patternCount = code.substring(pair.s-1, pair.s);
        if(!parseInteger(patternCount)) {
            code = code.replace(code.substring(pair.s-1, pair.e+1) , "");
        }else if(parseInteger(pattern)){
            Scanner scanner = new Scanner(System.in);
            System.out.println("Enter Code for : "+code.substring(pair.s-1, pair.e+1)  );
            String replacement  = "";
            pattern = scanner.nextLine();
            for(int i  =  0 ; i < Integer.parseInt(patternCount);i++){
                replacement =  replacement + pattern;
            }
            code = code.replace(code.substring(pair.s-1, pair.e+1) , replacement);
        }else{
            String replacement = "";
            for(int i  =  0 ; i < Integer.parseInt(patternCount);i++){
                replacement =  replacement + pattern;
            }
            code = code.replace(code.substring(pair.s-1, pair.e+1) , replacement);
        }
    }

    public static void main(String[] args) {
        boolean  decoding  =  false;
        do{
            Pair  pair = getPair();
            decoding = pair != null ? true : false;
            if(decoding){
                decode(pair);
            }

        }while(decoding);

        System.out.println(code);
    }
}
Community
  • 1
  • 1
shivam
  • 489
  • 2
  • 8
  • 22
0

I tried this problem using recursion, I think this is the correct approach to decode this:

Example : if we have a string 2[3[abc]]2[xy] then decode it in a level

1 level : 3[abc]3[abc]2[xy]

2 level : abcabcabc3[abc]2[xy]

3 level : abcabcabcabcabcabc2[xy]

4 level : abcabcabcabcabcabcxyxy

Demo code as below :

private static void decode(String value) {
    int startBracket=0;
    int endBracket=0;
    int encodedCount=0;
    int startIndex=0;
    String result="";
    StringBuilder temp=new StringBuilder();
    char[] data = value.toCharArray();
    for (int i = 0; i < data.length; i++) {
        if(data[i]=='['){
            if(encodedCount==0){
                encodedCount=Character.getNumericValue(data[i-1]);
                startIndex=i;
            }

            startBracket++;
            continue;
        }
        if(data[i]==']'){
            endBracket++;
        }
        if(startBracket==endBracket && startBracket!=0 && endBracket !=0){
            System.out.println(encodedCount);
            result=value.substring(0,startIndex-1);             
            String expandedTarget=value.substring(startIndex+1,i);
            String remainingEncodedValue = value.substring(i+1,value.length());
            System.out.println(expandedTarget);
            System.out.println(remainingEncodedValue);

            for (int j = 1; j <= encodedCount; j++) {
                temp.append(expandedTarget);
            }
            if(remainingEncodedValue.length()>1)
                temp.append(remainingEncodedValue);

            System.out.println("Decoded Result : "+result + temp.toString());
            decode(result + temp.toString());
            break;
        }
    }

}
Rahul Mahajan
  • 37
  • 1
  • 7