题目大意
按题目所给,判断一个方程式是否配平,样例若干
考点
括号匹配
思路
上图是题目给出的巴科斯范式,一定要结合文字完全看明白。
这个巴科斯范式主要是定义了一个层级关系,
化学方程式 = 表达式 ‘=’ 表达式
表达式 = 式子’+‘式子’+‘…’+‘式子
式子=系数+化学式
系数=不含前导0的十进制数
公式则比较复杂,涉及括号嵌套。
那么该怎么计算化学式中每个元素的出现次数呢?
例如:3A2(B(DC)3)4 (3为化学式的系数)
贡献为3*(2A+4(B+3*(D+C)))
想象一个指针正在正向扫描,每当指针穿过一层左括号,其内部的所有元素的都要在当前系数的基础上再乘以该括号的系数;而每当指针穿过一层右括号,都要在当前系数的基础上除以该括号的系数。
然后发现,这不就是栈吗?穿过左括号时,使用括号匹配找到右括号后面的系数,入栈,同时当前系数乘以该系数;穿过右括号时,取栈顶元素,当前系数除以该元素。
当然,也可以不用这个栈,穿过右括号时再把其后面的系数读一遍就好了。
可以发现上述过程是O(n^2)的,可以通过预处理优化到O(n),(从后往前,用栈存系数,记录每个左括号位置对应的系数),但本题没有卡时间。
易错点
本题没有陷阱,着重注意循环边界条件即可。
收获
括号匹配的写法
循环边界条件一定要想清楚
BNF要会看
满分代码
//!边界的地方,左边和右边不一样
//!循环遍历问题,迭代器的边界处理和自增
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
const int N=1e3+10;
typedef pair<int,int> PII;
int n,stk[N],stktop;//stk为栈
int main(){
cin>>n;
while(n--){
map<string,int>left,right;//clear?
string s;
cin>>s;
int len=s.length();
int i=0;
while(s[i]!='='){
char c=s[i];
int mul=1;
if(c=='+'){
i++;
continue;
}
else if(isdigit(c)){//发现系数
mul=0;
while(s[i]!='=' && isdigit(s[i])){
mul*=10;
mul+=s[i]-'0';
i++;
}
}
//然后开始匹配化学式,并计算原子个数,别忘了乘以mul
int j=i;
while(s[j]!='=' && s[j]!='+'){ //右边可能要变
if(isalpha(s[j])){ //括号外的情况
string ele;
if(isalpha(s[j+1]) && s[j+1]>='a' && s[j+1]<='z'){ //小写
ele=s.substr(j,2);
j+=2;
}
else ele=s.substr(j++,1);
//找系数
int submul=0;
if(isdigit(s[j])){
while(s[j]!='=' && isdigit(s[j])){
submul*=10;
submul+=s[j]-'0';
j++;
}
}
else submul=1;
//找到系数,更新答案
left[ele]+=mul*submul;
}
else{ //括号
if(s[j]=='('){
int k=j+1,pp=1;
while(pp){ //括号匹配
if(s[k]=='(')pp++;
else if(s[k]==')')pp--;
k++;
}
//成功匹配,找系数后缀
int kmul=0;
if(isdigit(s[k])){
while(s[k]!='=' && isdigit(s[k])){
kmul*=10;
kmul+=s[k]-'0';
k++;
}
}
else kmul=1;
//
stk[stktop++]=kmul;
mul *= kmul;
}
else if(s[j]==')'){
mul/=stk[--stktop];
}
j++;
}
}
i=j;
}
i++;
while(i<len){
char c=s[i];
int mul=1;
if(c=='+'){
i++;
continue;
}
else if(isdigit(c)){//发现系数
mul=0;
while(i<len && isdigit(s[i])){
mul*=10;
mul+=s[i]-'0';
i++;
}
}
//然后开始匹配化学式,并计算原子个数,别忘了乘以mul
int j=i;
while(j<len && s[j]!='+'){ //右边可能要变
if(isalpha(s[j])){ //括号外的情况
string ele;
if(j+1<len && isalpha(s[j+1]) && s[j+1]>='a' && s[j+1]<='z'){ //小写
ele=s.substr(j,2);
j+=2;
}
else ele=s.substr(j++,1);
//找系数
int submul=0;
if(j<len && isdigit(s[j])){
while(j<len && isdigit(s[j])){
submul*=10;
submul+=s[j]-'0';
j++;
}
}
else submul=1;
//找到系数,更新答案
right[ele]+=mul*submul;
}
else{ //括号
if(s[j]=='('){
int k=j+1,pp=1;
while(pp){ //括号匹配,不用找边界因为已经确保匹配成功!
if(s[k]=='(')pp++;
else if(s[k]==')')pp--;
k++;
}
//成功匹配,找系数后缀
int kmul=0;
if(k<len && isdigit(s[k])){
while(k<len && isdigit(s[k])){
kmul*=10;
kmul+=s[k]-'0';
k++;
}
}
else kmul=1;
//
stk[stktop++]=kmul;
mul *= kmul;
}
else if(s[j]==')'){
mul/=stk[--stktop];
}
j++;
}
}
i=j;
}
bool flag=1;
for(auto i:left){
if(right.count(i.fi) && i.se==right[i.fi]);
else{
flag=0;
break;
}
}
if(!flag){
cout<<"N\n";
continue;
}
for(auto i:right){
if(left.count(i.fi) && i.se==left[i.fi]);
else{
flag=0;
break;
}
}
if(flag)cout<<"Y\n";
else cout<<"N\n";
}
return 0;
}