String compression and decompression for data set that can be nested

Viewed 1348

My input is a compressed string of the format number[string] and the decompressed output form should be the string written number times. For example:

3[abc]4[ab]c = abcabcabcababababc

2[3[a]b] = aaabaaab.

My brute force approach in Java:

public class CompresnDecompresn {

    public static void main(String[] args) {

        String s="3[abc]4[ab]c";
        for (int i = 0; i<s.length(); i++)  {
            if(s.charAt(i)==']') {
                int j=i-1;
                while(true)  {
                    if(s.charAt(j)=='[') {
                        break;
                    }
                    j--;
                }
                int k=j-1;
                while(true) {
                    if(k<0) {
                        break;
                    }
                    int m=(int)s.charAt(k);
                    if(m<48 || m>57) {
                        break;
                    }
                    k--;
                }
                k++;
                int freq=Integer.parseInt(s.substring(k, j));
                String snippet=s.substring(j+1,i);
                String temp="";

                for (int l = 0; l < freq; l++) {
                    temp+=snippet;
                }
                s=s.substring(0,k)+temp+s.substring(i+1);
            }
        }        
        System.out.println(s);                
    }    
}

Can I get some better approach with less cost?

6 Answers

Since you use Java, feel free to use what Java offers to make the solution more readable and straightforward. Here takes Regex the place as well as concatenating String using the StringBuilder.

Consider the input String string ="3[abc]4[ab]c"; Personally, I'd go for something like this:

Firstly define the Couple class holding the pairs of number-letters.

private static class Couple {

    public int x;
    public String y;

    public Couple(int x, String y) {
        this.x = x;
        this.y = y;
    }
}

And here we go:

// Regex to extract the number before '[' and the content inside of '[]'
Pattern p = Pattern.compile("(\\d+)\\[(.*?)\\]");
Matcher m = p.matcher(string);

// Coupling the number and the letters
List<Couple> couples = new ArrayList<>();
while (m.find()) {
    couples.add(new Couple(Integer.parseInt(m.group(1)), m.group(2)));
}

// Concatenating String together using the for-cycle
String rest = string.substring(string.lastIndexOf("]")+1, string.length());
StringBuilder sb = new StringBuilder();

for (Couple c: couples) {
    for (int i=0; i<c.x; i++) {
        sb.append(c.y);
    }
}

// Enjoying the result
sb.append(rest);
System.out.println(sb.toString());

Please remember, this is just a dumb naïve solution to your problem working in case there is always the input string in the format of

number[string]number[string]number[string]rest // more of number[string] pairs

You have to think in wide whether there are places to be aware of failing code. What if there is no rest? What if the user's input will not be of the same format (never trust users' input) - so isn't it worth to validate it against Regex? You have to ask questions to yourself beggining with what if something happens.

Anyway, you can start on my implementation and go on as you need.

We actually want to find the strings in [] and repeat it n times which is specified before the []. But the problem is these strings are nested. So when we call the function that reads the string in[] it should call itself again when it hits a new []. Hence this leads to a recursive solution and we loop through the input string only once.

public class StringDecomposer {

    public String process(String input) {

        StringBuilder result = new StringBuilder();

        decompres(input, 0, result);

        return result.toString();
    }

    private int decompres(String input, int ofset, StringBuilder result) {

        StringBuilder rpt = new StringBuilder();
        StringBuilder current = new StringBuilder();

        while(ofset < input.length()) {

            if(input.charAt(ofset) == '[') {
                ofset = decompres(input, ofset+1, current);
                repeat(rpt, current);
                result.append(current);
                rpt.delete(0, rpt.length());
                current.delete(0, current.length());
            }
            else if(input.charAt(ofset) == ']') {
                break;
            }
            else if(input.charAt(ofset) > 47 && 
                    input.charAt(ofset) < 58) {
                rpt.append(input.charAt(ofset));
            }
            else {
                current.append(input.charAt(ofset));
            }
            ofset++;
        }
        result.append(current);
        return ofset;
    }

    private void repeat(StringBuilder rpt, StringBuilder input) {

        if(rpt.length() > 0) {
            StringBuilder current = new StringBuilder(input);
            int times = Integer.parseInt(rpt.toString());
            for(int i = 1; i < times; i++) {
                input.append(current);
            }
        }
    }
}

I have used stack to solve the problem.

from collections import deque
st=input("Enter a compressed string: ")
myStack=deque()
numstring=""
alphastring=""
for i in range(len(st)):
     ch=st[i]
     print(myStack)
     if ch.isdigit():
          numstring+=ch
          if alphastring!="":
               myStack.append(alphastring)
               alphastring=""
     elif ch=='[':
          myStack.append(numstring)
          numstring=""
     elif ch.isalpha():
          alphastring+=ch
     elif ch==']':
          top=myStack.pop()
          if top.isnumeric():
               check=""
               m=int(top)
               word=alphastring*m
               if len(myStack)>0:
                    check=myStack.pop()
                    if(check.isalpha()):
                         word=check+word
                    else:
                         myStack.append(check)
               myStack.append(word)
               alphastring=""

          else:
               m=int(myStack.pop())
               myStack.append((top+alphastring)*m)
               alphastring=""
solution=alphastring
while(len(myStack)>0):
     solution=myStack.pop()+solution
print("Decompressed : ",solution)
@Test()
public void googleProblemSolve() {
    String mainString = "3[abc]4[ab]c10[a]9[x]15[d]";
    // Split the string using end bracket
    String sep[] = mainString.split("\\]");

    for (int i = 0; i < sep.length; i++) {
        // remove start bracket from the each index to get iteration count
        String[] subSep = sep[i].split("\\[");
        
        /* before part of the split string would be iteration count and second part
         would be the actual string*/
        repeatWords(subSep[0], subSep[1]);
    }

}

private void repeatWords(String iteration, String str) {
    int ite = 0;
    String newStr = str;

    // iteration value can throw exception if the string is like i.e. c10
    try {
        ite = Integer.parseInt(iteration);
    } catch (NumberFormatException e) {
        /*
         * consider the first char in the string would be the alphabet with no
         * repeatation
         */
        newStr = Character.toString(iteration.charAt(0));

        // check the length of the string;
        // if its three means iteration count is in 2 digit
        if (iteration.length() == 3) {
            ite = Character.getNumericValue(iteration.charAt(1) + iteration.charAt(2));
        } else {
            ite = Integer.parseInt(String.valueOf(iteration.charAt(1)));
        }
    }

    for (int i = 0; i < ite - 1; i++) {
        newStr = newStr + str;
    }
    System.out.print(newStr);
}

I guess this could be the optimal solution to this problem. Please share your thoughts.

public class decompress {
    public static void main(String s[]) {
        String str = "6b2c8t";
        int times = 0;
        char[] c = str.toCharArray();
        for(char i : c) {
            if(Character.isDigit(i))
                times  =Integer.parseInt(String.valueOf(i));
            for(int x=0;x<times;x++) {
                if(!Character.isDigit(i))
                    System.out.print(i);
            } 
            
        }
    }
    
}

This would work use it!!

Related