CSP201912-3 化学方程式

题目描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题目分析

题目要求判断配平,即统计每种元素的数量,且涉及处理括号的问题。而化学方程式最长可以达到1000字符,基本应当把程序时间复杂度限制到线性阶才比较稳妥,那么处理括号必然用到栈(递归次数超过10万好像会爆掉):

while() {
	do {
		Expr;
	} while(!stack.empty())
}

下面仔细分析化学方程式 (equation) 结构:equation 根据 ‘+’ 或 ‘=’ 拆分然后扒掉前面的系数得到化学式 (formula),利用题目描述中所谓的巴科斯范式 (BNF) 形式 formula 可以表述为 F = A 1 ∣ ( F ) ∣ A 2 F = A_1| (F) |A_2 F=A1(F)A2 的递归形式, A 1 、 A 2 A_1、A_2 A1A2 为连续元素名 (element) 构成的纯字母序列。
把题目中“项” (term) 的定义改为这种连续字母序列,以 A(A(AB)B)AB 为例,字母序列 X X X 只有三种情况:

  • formula中第一个 ‘(’ 之前的所有连续字母序列;
  • 某一个 ‘(’ 到下一个 ‘(’ 之间的所有连续字母序列;
  • formula中最后一个 ‘)’ 之后的所有连续字母序列;

至此将有三个函数来层层处理化学方程式的各级组成部分:

函数处理对象
equaParse(euation)equation
formuParse(formula)formula
termParse(term)(重新定义的)term

最后一个问题是如何给处在括号内部的元素计数乘上括号外的系数。显然我们遍历整个化学方程式的过程中,先获取到元素名称、继续向后遍历才能得到括号外要乘的系数, 正常的顺序逻辑无法实现,所以此处又要用到栈。但是栈内部需要存元素名称和数量,模板的stack似乎只能支持基本类型作为模板参数,所以此处用到了vector<pair<string, int>> 类型作为栈来使用。至此整个程序逻辑已完成。

源码和测试用例

源代码:

#include<iostream>
#include<cstring>
#include<cctype>
#include<string>
#include<unordered_map>
#include<deque>
#include<vector>
using namespace std;

void atomParse(vector<pair<string, int>> &atom, unordered_map<string, int> &e,\
	char* term, char* end, int coef, int &sgn, bool revise) {
		
	for(char* p = term; p < end; ++ p) {
		//extract elem name
		char elem[3] = {'\0'};
		if(isupper(*p)) {
			elem[0] = *p;
			if(p + 1 < end && islower(*(p + 1))) {
				++ p;
				elem[1] = *p;
			}
		} else continue;
		string name(elem);
		//extract suffix coef
		int coef2 = 0, base = 10; 
		while(p < end && isdigit(*(++ p))) {
			coef2 = coef2 * base + *p - '0';
		}
		-- p;
		if(coef2 == 0) coef2 = 1;
		int k = coef * coef2 *  sgn;
		if(revise) {
			atom.push_back(make_pair<string&, int&>(name, k));
//			cout <<"atom in stack: " <<name <<" " <<"len(term): " <<strlen(term) <<" " <<"end - term: " <<end - term <<endl;
		} else {
			if(e.empty() || e.find(name) == e.end()) e.insert(make_pair<string&, int&>(name, k));
			else e[name] += k; 
//			if(sgn > 0) cout <<"atom not in stack: " <<name <<endl;
		}
	}
}
void formuParse(unordered_map<string, int> &e, char* formula, int &sgn) {
	char *p = formula, *end = formula + strlen(formula);
	//extract prefix coef:
	int coef = 0, base = 10;
	while(isdigit(*p)) {
		coef = coef * base + *p - '0';
		++ p;
	}
	if(coef == 0) coef = 1;
	while(p < end) {
		deque<int> deq;
		deq.push_back(0);
		vector<pair<string, int>> atom;
		do {
			if(*p == '(') {
				++ p;
				char *term = p;
				while((isalpha(*p) || isdigit(*p)) && p < end) ++ p;
				atomParse(atom, e, term, p, coef, sgn, true);
				deq.push_back(atom.size());
				-- p;
			} else if(*p == ')') {
				int kk = 0;
//				++ p;
				while(isdigit(*(++ p))) {
					kk = kk * base + *p - '0';
				}
				if(kk == 0) kk = 1;
				int right = deq.back();
				deq.pop_back();
				for(int i = deq.back(); i < right; ++ i) {
					atom[i].second *= kk;
				}
				deq.pop_back();
				//为什么term值应该是 p - 1? 
				char *term = p - 1;
				while((isalpha(*p) || isdigit(*p)) && p < end) ++ p;
				if(p != end) {
					atomParse(atom, e, term, p, coef, sgn, true);
					deq.push_back(atom.size());
				} else p = term;
				-- p;
			} else {
				char *term = p;
				while((isalpha(*p) || isdigit(*p)) && p < end) ++ p;
				atomParse(atom, e, term, p, coef, sgn, false);
				-- p;
			}
			++ p;
		} while(!deq.empty() && p < end);
		while(!atom.empty()) {
			string name = atom.back().first;
			if(e.empty() || e.find(name) == e.end()) e.insert(make_pair<string&, int&>(name, atom.back().second));
			else e[name] += atom.back().second;
			atom.pop_back();
		}
		++ p;
	}
}
void equaParse(unordered_map<string, int> &e, char* equa) {
	char *end = equa + strlen(equa), *neg = end;
	for(char *p = equa; p < end; ++ p) {
		char *formula = p;
		while(*p != '+' && *p != '=' && p < end) {
			++ p;
		}
		if(*p == '=') neg = p;
		*p = '\0';
		int sgn = p > neg? -1 : 1;
		formuParse(e, formula, sgn);
	}
}
int main() {
	int n;
	scanf("%d\n", &n);
	for(int i = 0; i < n; ++ i) {
		char equation[2555];
		gets(equation);
		unordered_map<string, int> atoms;
		equaParse(atoms, equation);
		bool yes = true;
//		for(auto &it : atoms) {
//			cout <<it.first <<": " <<it.second <<endl;
//		}
		for(auto &it : atoms) {
			if(it.second != 0) {
				yes = false;
				cout <<"N\n";
				break;
			}
		}
		if(yes) cout <<"Y\n";
	}
	return 0;
}

测试用例:

output: Y Y Y Y Y Y N Y Y Y Y Y Y Y
14
2H2+O2=2H2O
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
Cu+As=Cs+Au
8Au+16NaCN+4H2O+2O2=4(NaAu(CN)2)2+8NaOH
8Au+16NaCN+4H2O+2O2=4(Au(CN)2Na)2+8NaOH
4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH
3(B(A(A)2B)3A)4=120A+48B
3(B(((A((A))2B)))3A)4=120A+48B
3(B(((A((A)2)2B)))3A)4=192A+48B
3A(A(A(A(A(((A))B2)2AB)2A)2A)2A)2A=3AA+6AA+12AA+24AA+48AAB+96ABB
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值