sssemil
sssemil

Reputation: 278

Separate atoms from given chemical formula programmatically

I'm trying to solve this - https://www.e-olymp.com/en/problems/7549 but I can't pass all tests (passed only 1/4). I was testing my code with these inputs:

(AlC2)3Na4
3Al+6C+4Na

Gh(G2H3(H1A45)2)5(Bn6Mn3)5
450A+30Bn+10G+Gh+25H+15Mn

(Na1000)10Na02
10002Na

So, looks like it works but it does not work. Any hint would be great.

Here is problem itself: The chemical formula of a molecule M describes its atomic make-up. Chemical formulas obey the following grammar:

M := G | M G

G := S | S C

S := A | '(' M ')'

C := T | N E

E := D | D E

T := '2' | ... | '9'

N := '1' | ... | '9'

D := '0' | .. | '9'

A := U | U L | U L L

U := 'A' | .. | 'Z'

L := 'a' | .. | 'z'

The count C represents a multiplier for the subgroup S that precedes it. For example, H2O has two H (hydrogen) and one O (oxygen) atoms, and (AlC2)3Na4 contains 3 Al (aluminum), 6 C (carbon) and 4 Na (sodium) atoms.

Input

Contains multiple test cases. For each test case, there will be one line, containing a valid chemical formula. Each line will have no more than 100 characters.

Output

For each line there will be one line of output which is the atomic decomposition of the chemical in the form of a sum as shown in the sample output. The atoms are listed in lexicographical order, and a count of 1 is implied and not explicitly written. There are no blank spaces in the output. All of the counts in the correct output will be representable in 32-bit signed integers.

And this is my code (it might looks disgusting but anyway):

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
//Gh(G2H3(H1A45)2)5(Bn6Mn3)5

public class Main {

    static Scanner mIn;
    static PrintWriter mOut;

    public static void main(String[] args) {
        mIn = new Scanner(System.in);
        mOut = new PrintWriter(System.out);

        String line = mIn.nextLine();

        ArrayList<Atom> atoms = new ArrayList<>();

        ArrayList<Integer> startBr = new ArrayList<>();
        for (int i = 0; i < line.length(); i++) {
            if (line.charAt(i) == '(') {
                //starting
                startBr.add(atoms.size());
            } else if (line.charAt(i) == ')') {
                //ending
                int n = 1;

                if (line.length() > i + 1 && isNum(line.charAt(i + 1))) {
                    n = line.charAt(i + 1) - '0';
                    i++;
                    while (line.length() > i + 1 && isNum(line.charAt(i + 1))) {
                        n *= 10;
                        n += line.charAt(i + 1) - '0';
                        i++;
                    }
                }

                for (int j = startBr.get(startBr.size() - 1); j < atoms.size(); j++) {
                    atoms.get(j).n *= n;
                }

                startBr.remove(startBr.size() - 1);
            } else if (Character.isUpperCase(line.charAt(i))) {
                Atom atom = new Atom();
                atom.name = String.valueOf(line.charAt(i));
                if (line.length() > i + 1 && isCont(line.charAt(i + 1))) {
                    atom.name += String.valueOf(line.charAt(i + 1));
                    i++;

                    if (line.length() > i + 1 && isNum(line.charAt(i + 1))) {
                        atom.n = line.charAt(i + 1) - '0';
                        i++;

                        while (line.length() > i + 1 && isNum(line.charAt(i + 1))) {
                            atom.n *= 10;
                            atom.n += line.charAt(i + 1) - '0';
                            i++;
                        }
                    }
                }

                if (line.length() > i + 1 && isNum(line.charAt(i + 1))) {
                    atom.n = Integer.parseInt(String.valueOf(line.charAt(i + 1)));
                    i++;
                    while (line.length() > i + 1 && isNum(line.charAt(i + 1))) {
                        atom.n *= 10;
                        atom.n += line.charAt(i + 1) - '0';
                        i++;
                    }
                }
                atoms.add(atom);
            }
        }

        for (int i = 0; i < atoms.size(); i++) {
            for (int j = i + 1; j < atoms.size(); j++) {
                if (atoms.get(i).name.equals(atoms.get(j).name)) {
                    atoms.get(i).n += atoms.get(j).n;
                    atoms.get(j).n = -1;
                }
            }
        }

        for (int i = 0; i < atoms.size(); i++) {
            if (atoms.get(i).n < 1) {
                atoms.remove(i);
            }
        }

        Collections.sort(atoms, ALPHABETICAL_ORDER);

        for (int i = 0; i < atoms.size(); i++) {
            p(atoms.get(i).toString());
            if (i != atoms.size() - 1) {
                p("+");
            }
        }
    }

    private static Comparator<Atom> ALPHABETICAL_ORDER = new Comparator<Atom>() {
        public int compare(Atom atom1, Atom atom2) {
            return atom1.name.compareTo(atom2.name);
        }
    };

    private static boolean isCont(char c) {
        return c >= 'a' && c <= 'z';
    }

    private static boolean isNum(char c) {
        return c >= '0' && c <= '9';
    }

    private static void p(Object obj) {
        mOut.print(obj);
        mOut.flush();
    }

    private static class Atom {
        public String name;
        public int n = 1;

        public String toString() {
            if (n == 1) {
                return name;
            }
            return n + name;
        }
    }
}

Upvotes: 0

Views: 524

Answers (1)

Stefan Haustein
Stefan Haustein

Reputation: 18813

Have you tried Na123 or O1234 as input? Both seem to be permitted by the grammar?

BTW: Some of the methods could be much shorter...

private static boolean isCont(char c) {
  return c >= 'a' && c <= 'z';
}

private static isNum(char c) {
  return c >= '0' && c <= '9';
}

For converting a single digit character c to the corresponding value, you could use Character.getNumericalValue(c) -- or just (c - '0'). The Character class javadoc probably contains more interesting hints for your use case...

P.S. I don't really understand why you don't use the same number parsing code after conditionally adding the cont letter....

Let me throw in a code fragment you might find useful:

while (line.length() > i + 1 && isNum(line.charAt(i + 1))) {
  atom.n = 10 * atom.n + (line.charAt(++i) - '0');
}

Upvotes: 1

Related Questions