ccf第18次认证_3.化学方程式(Java)

Posted 卷王2048

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ccf第18次认证_3.化学方程式(Java)相关的知识,希望对你有一定的参考价值。

ccf第18次认证_3.化学方程式

化学方程式,也称为化学反应方程式,是用化学式表示化学反应的式子。

给出一组化学方程式,请你编写程序判断每个方程式是否配平(也就是方程式中等号左右两边的元素种类和对应的原子个数是否相同)。

本题给出的化学方程式由大小写字母、数字和符号(包括等号 =、加号 +、左圆括号 ( 和右圆括号 ))组成,不会出现其他字符(包括空白字符,如空格、制表符等)。

化学方程式的格式与化学课本中的形式基本相同(化学式中表示元素原子个数的下标用正常文本,如 H2OH2O 写成 H2OH2O),用自然语言描述如下:

  • 化学方程式由左右两个表达式组成,中间用一个等号 = 连接,如 2H2+02=2H20;
  • 表达式由若干部分组成,每部分由系数化学式构成,部分之间用加号 + 连接,如 2H2+022H20;
  • 系数整数空串,如为空串表示系数为 11;
  • 整数由一个或多个数字构成;
  • 化学式由若干部分组成,每部分由系数构成,部分之间直接连接,如 H20CO2Ca(OH)2Ba3(PO4)2;
  • 元素或用左右圆括号括起来的化学式,如 HCa(OH)(PO4);
  • 元素可以是一个大写字母,也可以是一个大写字母跟着一个小写字母,如 HOCa

用巴科斯范式(Backus-Naur form,BNF)给出的形式化定义如下:

<equation> ::= <expr> "=" <expr>
<expr> ::= <coef> <formula> | <expr> "+" <coef> <formula>
<coef> ::= <digits> | ""
<digits> ::= <digit> | <digits> <digit>
<digit> ::= "0" | "1" | ... | "9"
<formula> ::= <term> <coef> | <formula> <term> <coef>
<term> ::= <element> | "(" <formula> ")"
<element> ::= <uppercase> | <uppercase> <lowercase>
<uppercase> ::= "A" | "B" | ... | "Z"
<lowercase> ::= "a" | "b" | ... | "z"

输入格式

输入的第一行包含一个正整数 n,表示输入的化学方程式个数。

接下来 n 行,每行是一个符合定义的化学方程式。

输出格式

输出共 n 行,每行是一个大写字母 YN,回答输入中相应的化学方程式是否配平。

数据范围

1≤n≤100,
输入的化学方程式都是符合题目中给出的定义的,且长度不超过 1000
系数不会有前导零,也不会有为零的系数。
化学方程式的任何一边,其中任何一种元素的原子总个数都不超过 10^9
QQ截图20210224142256.png

输入样例:

11
H2+O2=H2O
2H2+O2=2H2O
H2+Cl2=2NaCl
H2+Cl2=2HCl
CH4+2O2=CO2+2H2O
CaCl2+2AgNO3=Ca(NO3)2+2AgCl
3Ba(OH)2+2H3PO4=6H2O+Ba3(PO4)2
3Ba(OH)2+2H3PO4=Ba3(PO4)2+6H2O
4Zn+10HNO3=4Zn(NO3)2+NH4NO3+3H2O
4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH
Cu+As=Cs+Au

输出样例:

N
Y
N
Y
Y
Y
Y
Y
Y
Y
N

题解

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * @see dfs 表达式处理 https://www.acwing.com/problem/content/description/3287/
 */
public class Main {

    static Scanner sc = new Scanner(System.in);
    static int u;
    static Map<String, Integer> map = new HashMap<>();

    static Map<String, Integer> dfs(String formula) {
        Map<String, Integer> res = new HashMap<>();
        while (u < formula.length()) {
            Map<String, Integer> temp;
            if (formula.charAt(u) == '(') {
                u++;
                //把答案填进去
                temp = dfs(formula);
                u++;
                //处理括号后的角标
                int coefficient = 1;
                int k = u;
                while (k < formula.length() && Character.isDigit(formula.charAt(k))) {
                    k++;
                }
                if (k > u) {
                    coefficient = Integer.parseInt(formula.substring(u,k));
                    u=k;
                }
                for (String key : temp.keySet()) {
                    res.put(key, res.getOrDefault(key, 0) + coefficient * temp.get(key));
                }
            } else if (formula.charAt(u) == ')') {
                break;
            } else {
                //处理元素
                int k = u+1;
                while (k<formula.length()&&formula.charAt(k) >= 'a' && formula.charAt(k) <= 'z') {
                    k++;
                }
                String element = formula.substring(u,k);
                u =k;
                //处理括号内的角标
                int angleMark = 1;
                while (k<formula.length()&&Character.isDigit(formula.charAt(k))) {
                    k++;
                }
                if (k > u) {
                    angleMark = Integer.parseInt(formula.substring(u, k));
                    u = k;
                }
                res.put(element, res.getOrDefault(element, 0) + angleMark);
            }
        }
        return res;
    }


    public static Map<String, Integer> work(String ss) {
        Map<String,Integer> res = new HashMap<>();
        //双指针算法分割出+左右的每个化学式
        for (int i = 0; i < ss.length(); i++) {
            int j = i + 1;
            while (j < ss.length() && ss.charAt(j) != '+') {
                j++;
            }
            String formula = ss.substring(i, j);
            i = j;
            //处理每个化学式前的系数
            int coefficient = 1,k = 0;
            while (k<formula.length()&&Character.isDigit(formula.charAt(k))){
                k++;
                
            }
            if (k>0){
                coefficient = Integer.parseInt(formula.substring(0,k));
            }
            Map<String ,Integer> sign = dfs(formula.substring(k));
            u = 0;
            for (String key:sign.keySet()){
                res.put(key,res.getOrDefault(key,0)+coefficient*sign.get(key));
            }
        }
        return res;
    }

    public static void main(String[] args) throws Exception {
        int n = sc.nextInt();
        while (n-- > 0) {
           String equation = sc.next();
            int k = 0;

            for (int i = 0; i < equation.length(); i++) {
                if (equation.charAt(i) == '=') {
                    k = i;
                    break;
                }
            }
            Map<String, Integer> left = work(equation.substring(0, k));
            Map<String, Integer> right = work(equation.substring(k + 1));
            //比较两个Map是否完全相等用Map中重写后的equals方法
            //equals一般可以比较两个对象是否完全相等
            //== 一般比较两个变量是否完全相等
            if (left.equals(right)) {
                System.out.println("Y");
            } else {
                System.out.println("N");
            }
        }
    }
}


以上是关于ccf第18次认证_3.化学方程式(Java)的主要内容,如果未能解决你的问题,请参考以下文章

CCF-CSP刷题库11-18

2022 ccf c时间

第16次CCF CSP认证-第5题-317 号子任务(subtask317)-图论最短路径

CCF201912-3 化学方程式(100分)文本处理

CCF-CSP题解 201912-3 化学方程式

CCF能力认证历届第二题