参考

https://blog.csdn.net/wingrez/article/details/85111975

https://blog.csdn.net/richenyunqi/article/details/104147851

题解一:

化学方程式,也称为化学反应方程式,是用化学式表示化学反应的式子。给出一组化学方程式,请你编写程序判断每个方程式是否配平(也就是方程式中等号左右两边的元素种类和对应的原子个数是否相同)。
本题给出的化学方程式由大小写字母、数字和符号(包括等号=、加号+、左圆括号和右圆括号)组成,不会出现其他字符(包括空白字符,如空格、制表符等),化学方程式的格式与化学课本中的形式基本相同(化学式中表示元素原子个数的下标用正常文本,如H2O写成H2O),用自然语言描述如下:

化学方程式由左右两个表达式组成,中间用一个等号三连接,如2H2+O2=2H2O;
表达式由若干部分组成,每部分由系数和化学式构成,部分之间用加号+连接,如2H2+O2、2H2O;
系数是整数或空串,如为空串表示系数为1;
整数由一个或多个数字构成;
化学式由若干部分组成,每部分由项和系数构成,部分之间直接连接,如H2O、CO2、Ca(OH)2、Ba3(PO4)2;
项是元素或用左右圆括号括起来的化学式,如H、Ca、(OH)、(P04);
元素可以是一个大写字母,也可以是一个大写字母跟着一个小写字母,如H、O、Ca。

1、总体上判断是否配平,也就是方程式中等号左右两边的元素种类和对应的原子个数是否相同用到map<string,int> mp1,mp2 存储要求mp1.size()==mp2.size()且要遍历mp1,mp2bool judge(map<string,int> &left,map<string,int> &right){if(left.size()!=right.size()) return false;for(auto it=left.begin();it!=left.end();it++){if(right[it->first]!=it->second) return false;}return true;
}2、化学方程式由左右两个表达式组成,中间用一个等号三连接,如2H2+O2=2H2O;表达式由若干部分组成,每部分由系数和化学式构成,部分之间用加号+连接,如2H2+O2、2H2O;“=”“+”需要拆分stringstringstream ss(str);getline(ss,lstr,'=');getline(ss,rstr);加号的拆分要做循环,每次拆分得到一个化学式。stringstream ss(str);string item;while(getline(ss,item,'+')){...}3、整数由一个或多个数字构成;写个函数把整数读取的过程分离出来,这里位置pos要&int toNumber(string str,int &pos){int num=0;while(isdigit(str[pos])){num=num*10+str[pos]-'0';pos++;}return num;}4、接下来对化学式进行处理,分解成(元素,个数)的vectorstruct Elem{ //元素 string name; //名称 int num; //个数 Elem(string _name, int _num): name(_name), num(_num){}};vector<Elem> arr; //存储化学式的分解序列, 如 Ba、(、(、O、H、)、(、C、O、)、)化学式由若干部分组成,每部分由项和系数构成,部分之间直接连接,如H2O、CO2、Ca(OH)2、Ba3(PO4)2;系数是整数或空串,如为空串表示系数为1;整数由一个或多个数字构成;项是元素或用左右圆括号括起来的化学式,如H、Ca、(OH)、(P04);元素可以是一个大写字母,也可以是一个大写字母跟着一个小写字母,如H、O、Ca。当对化学式进行处理时,操作为:①    化学式首部系数   取出if(isdigit(item[i])) factor=toNumber(item,i);开始循环,②    是大写字母,接着往下读一个是不是小写字母 arr.push_back(Elem(name,1));③    是(                                  arr.push_back(Elem("(",0));④    是 )                                 arr.push_back(Elem(")",0));if(!isdigit(item[i+1])) item.insert(i+1,"1");⑤    是数字如果arr前面一个是元素,更改此元素的num如果arr前面是),遍历一直往前找到(,期间更改所有元素num,处理完后将()改成*表示已处理。注意事项:元素后没数字没问题,因为push时写了1,但是右括号后如果没数字,要插入一个1.

首先要清楚系数出现位置的三种情况:
1、整个化学式的首部
2、元素的右部
3、右括号的右部
如32Ba((OH)2(CO3)2)3(暂不考虑化学式的合法性)
我们从系数入手,在第一种情况下,该系数作用于化学式中的所有元素;在第二种情况下,该系数作用于紧接着的左边的元素;在第三种情况下,该系数作用于紧接着的左边的匹配括号里的所有元素,请通过上例理解。
为此,我们考虑使用一个数组将化学式的各部分存储起来arr,实现逻辑如下:
1、顺序遍历化学式
2、计算系数的第1种情况,也就是整个化学式的系数factor,继续遍历。
3、遇到左或右括号时,将左或右括号加入到arr中;遇到大写字母时,获取元素名称,将元素名称加入到arr中;遇到数字时,不存到arr中,根据系数的第2、3种情况相应处理(第1种情况已经在第二步处理完成)。
4、对于系数的第2种情况,此时数组arr的最后一个元素就是元素名称,系数作用于它即可;对于系数的第3种情况,从数组尾部逆序遍历,直到遇到左括号,将系数作用于这个范围中的元素,同时要将这一对匹配括号从数组中删除。
至此处理化学式的过程结束。
对于整个化学方程式,将其从等号两边分开处理。使用两个map分别记录左右两边的元素个数,再进行比较。
————————————————
版权声明:本文为CSDN博主「wingrez」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wingrez/article/details/103551680

#include<bits/stdc++.h>
using namespace std;
struct Elem{ //元素 string name; //名称 int num; //个数 Elem(string _name, int _num): name(_name), num(_num){}
};
int toNumber(string str,int &pos){int num=0;while(isdigit(str[pos])){num=num*10+str[pos]-'0';pos++;}return num;
}
void calc(string &str,map<string,int> &mp){stringstream ss(str);string item;while(getline(ss,item,'+')){vector<Elem> arr; //存储化学式的分解序列, 如 Ba、(、(、O、H、)、(、C、O、)、)int factor=1;int i=0;if(isdigit(item[i])) factor=toNumber(item,i);while(i<item.size()){if(isdigit(item[i])){int num=toNumber(item,i);if(arr[arr.size()-1].name==")"){int j=arr.size()-1;arr[j].name="*";while(arr[--j].name!="("){arr[j].num*=num;}arr[j].name="*";}else arr[arr.size()-1].num*=num;}else if(item[i]=='('){arr.push_back(Elem("(",0));i++;}else if(item[i]==')'){arr.push_back(Elem(")",0));if(!isdigit(item[i+1])) item.insert(i+1,"1");i++;}else if(isupper(item[i])){string name="";name+=item[i];i++;if(islower(item[i])){name+=item[i];i++;}
//              arr.push_back(name,1);arr.push_back(Elem(name,1));}}for(int i=0;i!=arr.size();++i){if(arr[i].name=="*") continue;mp[arr[i].name]+=arr[i].num*factor;}}
}
bool judge(map<string,int> &left,map<string,int> &right){if(left.size()!=right.size()) return false;for(auto it=left.begin();it!=left.end();it++){if(right[it->first]!=it->second) return false;}return true;
}
int main(){int n;scanf("%d",&n);for(int i=0;i<n;i++){map<string,int> left,right;string str,lstr,rstr;cin>>str;stringstream ss(str);getline(ss,lstr,'=');getline(ss,rstr);calc(lstr,left);calc(rstr,right);if(judge(left,right)) cout<<"Y"<<endl;else cout<<"N"<<endl;}return 0;
}

定义了元素的结构体,用vector来存储元素和(),当()被处理后标记成*,成功规避了嵌套的难点~

还有元素后和)后不一定有系数的问题也被很好地处理了。

题解二:

利用unordered_map<string, int> ans存储整个化学方程式中出现的原子及其对应个数。
先按=将整个方程式分成两部分。左部分所有原子默认基本系数为1,右部分所有原子默认基本系数为-1。每部分最终的原子个数要乘上这个基本系数,这样处理完整个方程式中所有原子,如果配平成功所有原子对应个数应该均为0;否则有原子个数不为0 。
对于按=将分成的两部分,再按+分成多个化学式。针对每个化学式统计每种原子出现的个数。那么如何处理带()的化学式呢?我们可以采取递归处理的方法,针对遇到的每个(,找出其对应的)的位置,递归处理该()内的化学式,注意此时该()内的系数要乘上该()后紧邻的数字。找出(对应的)的方法是,设立一个变量num,初始化为1,遍历(之后的所有字符,遇到一个(就让num加1,遇到一个)让num减1,那么使num==0的)字符即为(对应的)。
————————————————
版权声明:本文为CSDN博主「日沉云起」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/richenyunqi/article/details/104147851

find_if() 同 find() 一样,为在输入迭代器所定义的范围内查找单个对象的算法,它可以在前两个参数指定的范围内查找可以使第三个参数指定的谓词返回 true 的第一个对象。谓词不能修改传给它的对象。find_if() 会返回一个指向被找到对象的迭代器,如果没有找到对象,会返回这个 序列的结束迭代器。可以按如下方式使用 find_if() 来查找 numbers 中第一个大于 value 的元素:
int value {5};
auto iter1 = std::find_if(std::begin(numbers), std::end(numbers),[value](int n) { return n > value; });if(iter1 != std::end(numbers))std::cout << *iter1 << " was found greater than " << value << ".\n";
find_if() 的第三个参数是一个 lambda 表达式的谓词。这个 lambda 表达式以值的方式捕获 value,并在 lambda 参数大于 value 时返回 true。这段代码会找到一个值为 46 的元素。
#include<bits/stdc++.h>
using namespace std;
int n;
string formula;
unordered_map<string,int> ans;
int computeDigit(int &first,int last){int i=0;for(;first<=last and isdigit(formula[first]); ++first)i=i*10+formula[first]-'0';return i==0?1:i;
}
void f(int first,int last,int e){if(first==last or (last-first==1 and islower(formula[last]))){ans[formula.substr(first,last-first+1)]+=e;return;}e*=computeDigit(first,last);for(int i=first,j=i+1;i<=last;i=j,++j){if(isupper(formula[i])){if(j<=last and islower(formula[j])){++j;}int k=j;f(i,k-1,e*computeDigit(j,last));//递归处理 }else if(formula[i]=='('){for(int num=1;num!=0;++j){if(formula[j]=='(') ++num;else if(formula[j]==')') --num;}int k=j;f(i+1,k-1,e*computeDigit(j,last));} }
}
void expression(int first,int last,int e){for(int i=first,j=first;i<=last;i=j+1){j=formula.find('+',i);if(j==string::npos or j>last)j=last+1;f(i,j-1,e);}
}
int main(){cin>>n;while(n--){cin>>formula;ans.clear();int k=formula.find("=");expression(0,k-1,1);expression(k+1,formula.size()-1,-1);auto i=find_if(ans.begin(),ans.end(),[](const pair<string,int> &p) {return p.second!=0;});cout<<((i==ans.end())?"Y":"N")<<"\n"; }return 0;
}

使用了递归写法,还有一些处理和第一种都不同,学到很多了~

总结:

1、字符串的拆分

stringstream ss(s);
string item;
while(getline(ss, item, ','))
{elements.push_back(item);
}
int k=formula.find("=");
expression(0,k-1,1);
expression(k+1,formula.size()-1,-1);void expression(int first,int last,int e){for(int i=first,j=first;i<=last;i=j+1){j=formula.find('+',i);if(j==string::npos or j>last)j=last+1;f(i,j-1,e);}
}

2、对比左右两边相同与否的方法(相等or为0)

bool judge(map<string,int> &left,map<string,int> &right){if(left.size()!=right.size()) return false;for(auto it=left.begin();it!=left.end();it++){if(right[it->first]!=it->second) return false;}return true;
}
auto i=find_if(ans.begin(),ans.end(),[](const pair<string,int> &p) {return p.second!=0;});
cout<<((i==ans.end())?"Y":"N")<<"\n"; 

3、处理系数为1时不出现问题(逻辑上处理or默认取0),注意getnum时统一都是&pos

逻辑上判断:(这里的系数值得是后缀)元素后面没有系数   直接写1元素后面有系数     系数处理)后面没系数       在后面插入1)后面有系数       系数处理
将没有系数情况->有系数情况。
int computeDigit(int &first,int last){int i=0;for(;first<=last and isdigit(formula[first]); ++first)i=i*10+formula[first]-'0';return i==0?1:i;
}

4、处理嵌套

对于系数的第2种情况,此时数组arr的最后一个元素就是元素名称,系数作用于它即可;
对于系数的第3种情况,从数组尾部逆序遍历,直到遇到左括号,将系数作用于这个范围中的元素,同时要将这一对匹配括号从数组中删除。
我们可以采取递归处理的方法,针对遇到的每个(,找出其对应的)的位置,递归处理该()内的化学式,注意此时该
()内的系数要乘上该()后紧邻的数字。找出(对应的)的方法是,设立一个变量num,初始化为1,遍历(之后的所有
字符,遇到一个(就让num加1,遇到一个)让num减1,那么使num==0的)字符即为(对应的)。
递归处理void f(int first,int last,int e){//处理单个元素,递归终点if(first==last or (last-first==1 and islower(formula[last]))){ans[formula.substr(first,last-first+1)]+=e;return;}//顺序处理e*=computeDigit(first,last);//基本系数for(int i=first,j=i+1;i<=last;i=j,++j){//每次j=i+1//这种情况下一次直接到递归终点if(isupper(formula[i])){if(j<=last and islower(formula[j])){++j;}int k=j;f(i,k-1,e*computeDigit(j,last));//递归处理}//左括号else if(formula[i]=='('){//找到对应的右括号,递归处理for(int num=1;num!=0;++j){if(formula[j]=='(') ++num;else if(formula[j]==')') --num;}int k=j;f(i+1,k-1,e*computeDigit(j,last));//递归处理} //注:所有的数字都在e*computeDigit中处理到了}
}

CCF201912-3 化学方程式*相关推荐

  1. 化学方程式作评、数学上成知识百科……那些“别人家的老师”有何特别?

    懂化学的网友一定知道,上面的两张图是化学方程式,可你知道吗?它不仅是一个方程式,还是一个老师对学生的品评. 近日,浙江金华宾虹中学一位化学老师吕沆菲,给同学用化学方程式作评,并对每个方程式做了解读,和 ...

  2. c语言判断化学方程式,下列是某同学写的六个化学方程式:①Mg+O2点燃.MgO2②C+O2点燃.CO...

    化学方程式是最重要的化学语言,正确.熟练地书写化学方程式是学习化学必需具备的重要基本功. 怎样书写化学方程式? 1.要遵循两个基本原则 (1)以客观事实为基础 化学方程式既然是化学反应的表达形式,显然 ...

  3. 有机物燃烧的化学方程式配平(洛谷P1994题题解,Java语言描述)

    题目要求 P1994题目链接 分析 我太菜了,亏得我高中时期还虐(or被虐)生化,如今多年过去竟然没分析出来坑点,我太菜了. 我开始盲目认为"元素守恒",所以对HHH原子数求和, ...

  4. crout分解计算例题_专题:化学方程式计算

    一. 根据化学方程式的简单计算 (1) 根据化学方程式计算的依据 化学方程式表达的信息之一是反应物与生成物之间在"遵循固定质量比"的前提下的质量守恒.根据这一信息,可以利用化学方程 ...

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

    试题编号: 201912-3 试题名称: 化学方程式 时间限制: 1.0s 内存限制: 512.0MB 问题链接:CCF201912-3 化学方程式 问题简述:(略) 问题分析:文本处理问题,按字符串 ...

  6. CSP化学方程式题解

    CSP化学方程式题解 题目描述 题目分析 本题主要考察栈的应用(也可以采用递归的方式,效率低),是一道与编译原理相关的题目. 首先我采用的是类似于哈希表的方法存储的元素位置及其数量,当然也可以用stl ...

  7. Python趣用—配平化学方程式

    不知不觉已经毕业多年了,不知道大家是否还记得怎么配平化学方程式呢?反正小编我是已经记不太清了,所以今天的文章除了分享如何用python配平化学方程式,顺带着还会复习一些化学方程式的知识,希望广大化学爱 ...

  8. PTA Easy chemistry 化学方程式等价判定 string 状态机

    7-5 Easy chemistry In this question, you need to write a simple program to determine if the given ch ...

  9. markdown化学方程式

    之前讲了markdown化学表达式,接下来我来讲讲如何用markdown写化学方程式. $2KMnO_4 \stackrel{\Delta}{=\!=\!=} K_2MnO_4 + MnO_2 + O ...

最新文章

  1. c#获取txt,word,excel文档内容方法
  2. 《代码敲不队》第八次团队作业:Alpha冲刺 第五天
  3. 【数据结构和算法笔记】分别用vector数组和链表实现图的邻接表
  4. 云免流控服务器响应失败,云免流服务器是否违法
  5. 遭遇掌控欲望极强的上司,郁闷的项目
  6. keepalived详解(三)——keepalived与Nginx配合实战
  7. HDU 3639 Hawk-and-Chicken
  8. 装双系统win10和android,教你安装Win10和安卓Android双系统(不是模拟器)
  9. 顶级域名 一级域名 二级域名 三级域名什么区别?
  10. 扒一扒“WEBP格式”的图片
  11. 虚拟内存设置在其他盘引发的问题(待解决)
  12. PDF如何裁剪页面,PDF裁剪页面的方法
  13. 软件开发过程与项目管理(16.项目结束)
  14. 计算电费(厦大PAT)
  15. opcache php7,让子弹飞~利用 OPcache 扩展提升 PHP7 性能 | Laravel 篇
  16. 用javascript教你打造一个免费的PC微信机器人《二》
  17. 智能手机集体“高反”,荣耀V10能否用AI给行业供氧?
  18. mysql最大句柄数_【mysql】最大连接数和最大句柄数的查询和修改
  19. STM32CubMx自学笔记(一)LED灯翻转
  20. 设计模式 —— 策略模式

热门文章

  1. Python3 将汉语转换为汉语拼音
  2. Android之内嵌PDF预览
  3. 论文阅读笔记——VulDeePecker: A Deep Learning-Based System for Vulnerability Detection
  4. Tether操纵市场了吗?
  5. 深度学习--深度信念网络(Deep Belief Network)
  6. Xmake v2.7.1 发布,更好的 C++ Modules 支持
  7. ip_forward参数对Linux内核转发影响分析
  8. 如何优雅地跟老板请假?
  9. 如何编写一条好的测试用例
  10. Java8新特性之- Lambda表达式和函数式接口