【CCF 201912-3】化学方程式(递归下降)

题目描述

第一行给出化学方程式的个数N,随后N行给出N个化学方程式。

化学方程式的BNF文法如下: 在这里插入图片描述

问题分析

类似于编译原理的语法分析,这里我们采用递归下降的方式解决问题。

接下来,我们将以下面这个方程式为例来讲解我的思路。

3Ba(OH)2+2H3PO4=Ba3(PO4)2+6H2O

deal_input(string input):处理输入的化学方程式

首先将化学方程式根据"="划分成左右两个Part,两个part分别对应一个map,保存<化学元素,个数>的映射。

这里我编写了一个split函数,利用字符串流和getline函数实现。(具体见代码)

随后上述化学方程式将分为3Ba(OH)2+2H3PO4Ba3(PO4)2+6H2O两部分。

deal_part():处理方程式的左(右)半部分

根据文法我们可以知道,方程式的左半部分的形式为<expr> + <expr> + <expr>,即许多由+分隔开的<expr>。因此我们使用split函数将每一个<expr>分割开。

上述方程式的左半部分分隔为3Ba(OH)22H3PO4,我们分别对其进行处理。

deal_expr(string expr):处理expr

我们知道expr<ceof> <formula>两部分组成。ceof是一串数字或者空串,我们使用字符串流可以很轻松地将expr分成两部分。

因此3Ba(OH)2就变成了3Ba(OH)2

deal_formula(int ceof, string formula):处理formula

我们知道formula由许多<term><ceof>组成,注意这里的<ceof>是后缀的形式。而<term>有两种形式:<element>(<formula>)

而本题的一大难点就在于如何分割众多的<term><ceof>

对于第一种情况:
需要注意element可能为2个字符。
调用deal_term(element, 系数ceof * 后缀ceof)

对于第二种情况:
需要截取括号当中的内容作为新的formula,进行递归调用:deal_formula(formula, 系数ceof * 后缀ceof)

对于系数ceof为3的Ba(OH)2,将调用deal_term(3, "Ba")deal_formula(6, "OH")

deal_term(int ceof, string element): 累加元素的个数

直接执行map[element] += ceof即可。

isEqual():判断两个map是否相等

若相等,则说明配平了;

测试数据

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

满分代码(C++11)

#include <iostream>
#include <cstring>
#include <vector>
#include <sstream>
#include <algorithm>
#include <unordered_map>
using namespace std;

int n, map_index;					// 方程式的个数N,map的下标
string input;						// 每行输入的化学方程式
unordered_map<string, int> mp[2];	// 两个map

void deal_part(string part);
void deal_expr(string expr);
void deal_formula(int ceof, string formula);
void deal_term(int ceof, string element);

/* 以flag分割str,将分割后的字符串集合存放到vec中 */
void split(string& str, vector<string>& vec, char flag)
{
    stringstream ss(str);
    string temp;

    while (getline(ss, temp, flag))
    {
        vec.push_back(temp);
    }
}

/* 以=分割化学方程式,分别处理 */
void deal_input(string input)
{
    vector<string> vec;
    split(input, vec, '=');

    // 处理左半部分
    map_index = 0;
    deal_part(vec[0]);
    // 处理右半部分
    map_index = 1;
    deal_part(vec[1]);
}

/* 处理expr+expr+expr */ 
void deal_part(string part)
{
    vector<string> vec;
    split(part, vec, '+');

    for(string& expr : vec)
    {
        deal_expr(expr);
    }
}

/* 处理<expr> ::= <ceof><formula> */ 
void deal_expr(string expr)
{
	// ceof不为空串的情况 
    if(isdigit(expr[0]))
    {
        int ceof = 1;
        string formula;

        stringstream ss(expr);
        ss >> ceof;
        ss >> formula;

        deal_formula(ceof, formula);	// ceof为系数ceof 
    }
	
	// ceof为空的情况 
    else deal_formula(1, expr);		// 系数为1 
}

/* 处理<formula> ::= <term><ceof> */ 
void deal_formula(int ceof, string formula)
{
    auto cur = formula.begin();

    while(cur != formula.end())
    {
        if(isupper(*cur))	// 大写字母,说明是 <term> ::= <element><ceof> 的情况  
        {
        	// 元素(如Ca) 在formula当中的起始位置和终止位置 
            string::iterator element_begin = cur, element_end;

            if(cur + 1 != formula.end() && islower(*(cur+1)))	// 若存在小写字母 
                element_end = cur + 2;
            else element_end = cur + 1;
			 
            string element(element_begin, element_end);
			
			// 不存在term的后缀ceof 
            if(element_end == formula.end() || !isdigit(*element_end))
            {
                deal_term(ceof, element);	// 元素的个数为:系数ceof 
                cur = element_end;
            }
            // 存在后缀ceof 
            else
            {
            	// ceof的位置迭代器 
                string::iterator ceof_begin = element_end, ceof_end;
                for(ceof_end = ceof_begin + 1; ceof_end != formula.end(); ++ceof_end)
                {
                    if(!isdigit(*ceof_end)) break;
                }
                string ceof_str(ceof_begin, ceof_end);
                int ceof_int = atoi(ceof_str.c_str());
                deal_term(ceof * ceof_int, element);	// 元素的个数为:系数ceof * 后缀ceof 
                cur = ceof_end;
            }
        }
        else if(*cur == '(')	// 是括号,说明是 <term> ::= (<formula>) 的情况 
        {
        	// 新的formula的起始位置和终止位置 
            string::iterator form_begin = cur + 1, form_end;
            int bracket_count = 1;
            for(form_end = cur + 1; form_end != formula.end(); ++form_end)
            {
                if(*form_end == '(') ++bracket_count;
                if(*form_end == ')') --bracket_count;
                if(bracket_count == 0) break;
            }
            string form(form_begin, form_end); 
			
			// 不存在后缀ceof 
            if(form_end + 1 == formula.end() || !isdigit(*(form_end+1)))
            {
                deal_formula(ceof, form);
                cur = form_end + 1;
            }
            // 存在后缀ceof 
            else
            {
                string::iterator ceof_begin = form_end + 1, ceof_end;
                for(ceof_end = ceof_begin + 1; ceof_end != formula.end(); ++ceof_end)
                {
                    if(!isdigit(*ceof_end)) break;
                }
                string ceof_str(ceof_begin, ceof_end);
                int ceof_int = atoi(ceof_str.c_str());
                deal_formula(ceof * ceof_int, form);
                cur = ceof_end;
            }
        }
    }
}

void deal_term(int ceof, string element)
{
//    cout << ceof << " " << element << "\n";
    mp[map_index][element] += ceof;
}

/* 判断两个哈希map是否相等 */ 
bool isEqual(unordered_map<string, int>& src, unordered_map<string, int>& dst)
{
	// 判断大小 
    if(src.size() != dst.size()) return false;
    
	// 注:pair为<key, value>的元组,key为pair.first,value为pair.second 
    for(auto pair : src)
    {
        auto it = dst.find(pair.first);		// 在dst中寻找这个key
        if(it == dst.end()) return false;	// 没有找到,则map不相等 
        if(it->second != pair.second) return false;		// 找到了,但是value不一样,map也不相等 
    }
    return true;
}

int main()
{
    cin >> n;
    cin.get();	// 注意吃掉第一行末尾的'\n',为getline做准备 

    for(int i=0; i<n; ++i)
    {
        mp[0].clear();			// 清空map 
        mp[1].clear();
        getline(cin, input);	// 读取一个化学方程式 
        deal_input(input); 
        cout << (isEqual(mp[0], mp[1]) ? 'Y' : 'N') << "\n";
    }

    return 0;
}
  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值