面试测试:实现解码算法

时间:2017-10-20 20:47:59

标签: java algorithm data-structures decoding string-parsing

我在亚马逊的采访中遇到了这个问题。

给定Java中的String作为输入3[a]2[bc]编写一个函数来解码它,因此输出应为" **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]

我尝试了以下方法,但不正确,因为它没有解决嵌套元素

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());
    }
}   

对此有什么正确的解决方案?

是否可以使用封装类来解码这个问题,如果你在格式中仔细观察它的问题,我们可以将它转换为解码器类的对象。 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] }

5 个答案:

答案 0 :(得分:2)

2[2[a]3[b]]等嵌套表达式缩减为aabbbaabbb可以通过 innermost redux (= reducable expression)来完成。

因此,请继续替换未使用的形式digit[letters],直到不能再减少任何形式。

因为这看起来只是一个草图:

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 [α] 3 [B]]
  2. 2 [AA3并[b]]
  3. 2 [aabbb]
  4. aabbbaabbb
  5. 基于模式匹配的具体解决方案。

    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);
    

    正如评论中所讨论的那样,基于堆栈的解决方案是优越的,但我发现它的工作量更多。

答案 1 :(得分:1)

可以更清洁,但似乎解决了提到的问题 更新我已更新代码以解决@JimMichel指出的问题 这会考虑数字的多个数字,并且不接受格式错误的输入。

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();  

}    

答案 2 :(得分:1)

考虑一下如果添加一些运算符会发生什么。也就是说,"3[a]2[bc]"变为3*[a] + 2*[bc]。如果您重新定义*运算符以表示&#34;重复,&#34;并且+运算符表示&#34;连接&#34;。

使用Shunting yard algorithm,您可以将字符串解析为后缀形式:3 a * 2 bc +。分流码可轻松处理嵌套表达式。例如,您的3[2[a]]4[b]变为3 2 a * * 4 b * +。关于postfix的好处是它评估起来非常简单。

一旦您确信正确生成后缀表单,您可以编写代码来评估后缀表达式(这非常简单),或者您可以修改您的分流码算法以在输出阶段进行评估。也就是说,不是将操作数和运算符输出到字符串,而是将操作数推送到堆栈上,无论何时输出运算符,都可以从堆栈中弹出操作数,应用运算符,然后将结果推送到堆栈中。所以你的输出步骤变为:

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

当你完成解析时,堆栈上应该有一个操作数,你可以输出它。

后缀方法的替代方法是创建binary expression tree,然后对其进行评估。另一种选择是写一个recursive descent parser,虽然除非你最近一直在处理表达式解析,否则在面试过程中你可能很难得到它。

答案 3 :(得分:0)

作为问题的假设是含糊不清的:

  

对于第二种情况,3 [2 [a]] - > 3 [aa] - &gt; aaaaaa

     

对于第三种情况,如果整数在方括号内 - 用户将   通常提供输入和解码。

     

对于第四种情况,如果整数在方括号之外,则从输出中删除此字符串。

请试试这段代码。我在评论中询问了一些问题,请澄清它们。

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);
    }
}

答案 4 :(得分:0)

我使用递归尝试了这个问题,我认为这是解码这个问题的正确方法:

  

示例:如果我们有一个字符串2 [3 [abc]] 2 [xy],那么在一个级别解码它

     
    

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

         
      

2级:abcabcabc3 [abc] 2 [xy]

             
        

3级:abcabcabcabcabcabc2 [xy]

                 
          

4级:abcabcabcabcabcabcxyxy

        
      
    
  

演示代码如下:

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;
        }
    }

}