以编程方式从给定的化学式中分离原子
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;
}
}
}
您是否尝试过 Na123
或 O1234
作为输入?两者似乎都是语法允许的?
顺便说一句:有些方法可以更短...
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');
}
我正在尝试解决这个问题 - 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;
}
}
}
您是否尝试过 Na123
或 O1234
作为输入?两者似乎都是语法允许的?
顺便说一句:有些方法可以更短...
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');
}