题目描述
题目分析
题目要求判断配平,即统计每种元素的数量,且涉及处理括号的问题。而化学方程式最长可以达到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
A1、A2 为连续元素名 (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