Search code examples
javachemistry

Expansion of Molecular notation of Chemical Formula


I looked around a bit for a solution to parsing out a chemical formula that contained molecular components that may have their own suffixes for the purposes of being able to parse a formula for its complete atomic fraction.

How would one do this in Java?


Solution

  • Not having been able to find a way to do this in short order (and not having done a fun algorithm in a while) I settled on a stack implementation, as it's actually less complicated than a math operation stack.

    Going backward through the stack, you only have to be aware of a few things, as this is fundamentally a modified implementation of a common solution to parsing a mathematical statement.

    1) Your integer suffix can be built of multiple characters
    2) Your integer suffix could be a multiplier (next character being a ")")
    3) You need to handle for implicit "1"s

    The implementation pops characters off one stack and pushes only letters and numbers onto your "return stack".

    String expandFormula(String s){
        Stack<Character> stack = new Stack();
        Stack<Character> hanoi = new Stack();
        char[] ca = s.toCharArray();
        Character c;
        List<Integer> multipliers = new ArrayList();
        String multiBuff;
    
        int val;
        boolean flag;
    
        for (int i = 0; i < ca.length; i++)
            stack.push(ca[i]);
    
        while(!stack.isEmpty()){
            c = stack.pop();
            if (Character.isLetter(c)){ 
                try{
                    //previous parse was end of Symbol, implicit "1"
                    flag = Character.isUpperCase(hanoi.peek());
                }
                catch(EmptyStackException ese){ //change exception
                    flag = false;
                }
                //push implicit 1
                if (flag){
                    stack.push(c);
                    stack.push('1');
                }
                //continue as usual
                else
                    hanoi.push(c);
            }
            //begin number parsing
        else if(Character.isDigit(c)){
                flag = false;
                multiBuff = c +"";
                //parse the integer out
                while(Character.isDigit(stack.peek())){
                    c = stack.pop();
                    multiBuff = c + multiBuff;
                }
                //if next char is ), then value is a suffix
                if (stack.peek() == ')'){
                    flag = true;
                    stack.pop();
                    multipliers.add(Integer.parseInt(multiBuff));
                    //pop successive )s
                    while(stack.peek() == ')'){
                        stack.pop();
                        multipliers.add(1);
                    }
                }
                if(Character.isLetter(stack.peek())){
                    val = flag ? 0 : Integer.parseInt(multiBuff);
                    //get full value of 
                    for(Integer i : multipliers){
                        if (val == 0)
                            val = i;
                        else
                            val *= i;
                    }
                    //trim and push first decibit
                    while(val > 0){
                            hanoi.push(Character.forDigit(val % 10, 10));
                            val /= 10;
                    }
                }
            }
            //end of nest, remove most recent multiplier
            else if(c == '(')
                try{
                    multipliers.remove(multipliers.size()-1);
                }
                catch(ArrayIndexOutOfBoundsException aioobe){
    
                }
        }
        multiBuff = "";
        while(!hanoi.isEmpty())
            multiBuff += hanoi.pop();
    
        return multibuff;        
    }
    

    This solution can be converted directly to your output string by:

    1) Change "hanoi" to string
    2) Change "hanoi.push(c)" to hanoi = c + hanoi
    3) Change "hanoi.peek()" to "hanoi.charAt(0)"
    4) Change Exceptions as necessary (or use general exceptions anyway)
    5) Just return hanoi instead of the multibuff thing at the bottom.