以编程方式从给定的化学式中分离原子

Separate atoms from given chemical formula programmatically

我正在尝试解决这个问题 - https://www.e-olymp.com/en/problems/7549 但我无法通过所有测试(仅通过 1/4)。我正在使用这些输入测试我的代码:

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

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

(Na1000)10Na02
10002Na

所以,看起来它有效,但它不起作用。任何提示都会很棒。

这是问题本身: 分子 M 的化学式描述了它的原子组成。化学式遵循以下语法:

M := G | MG

G := S | SC

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

C := T | NE

E := D | D E

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

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

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

一个:=你| UL | U L L

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

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

计数 C 表示它前面的子组 S 的乘数。例如,H2O 有两个 H(氢)和一个 O(氧)原子,而 (AlC2)3Na4 包含 3 个 Al(铝)、6 个 C(碳)和 4 个 Na(钠)原子。

输入

包含多个测试用例。对于每个测试用例,将有一行包含有效的化学式。每行不超过 100 个字符。

输出

对于每一行,将有一行输出,如示例输出中所示,该输出是化学物质的总和形式的原子分解。原子按字典顺序列出,隐含计数为 1 而未明确写入。输出中没有空格。正确输出中的所有计数都将用 32 位有符号整数表示。

这是我的代码(可能看起来很恶心,但无论如何):

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

您是否尝试过 Na123O1234 作为输入?两者似乎都是语法允许的?

顺便说一句:有些方法可以更短...

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

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

要将单个数字字符 c 转换为相应的值,您可以使用 Character.getNumericalValue(c) -- 或者只是 (c - '0')。字符 class javadoc 可能包含针对您的用例的更多有趣提示...

P.S. 我真的不明白为什么你在有条件地添加 cont 字母后不使用相同的数字解析代码....

让我插入一个您可能会觉得有用的代码片段:

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