CCF-CSP 化学方程式
提醒:下面的代码在Dev(C++)模式下编译出错,但是我真的看不出来哪里不对,并且本地用同样的标准编译没问题;而用PAT的OJ编译也是没问题的。所以我用CCF的C++11模式去提交,就100了。很奇怪吧?如果有热心人看出了我哪里有问题,欢迎评论告诉我,谢谢!
思路:是CCF的一贯风格:字符串解析。这题我感觉比JSON查询的难度大,因为这题要找出用栈保存什么还是挺难的。虽然出栈和入栈时机很明显,就是碰到左右括号嘛。最后发现原来需要用栈保存当前列表的化学元素的计数(用一个map表示)。然后就很自然了。
至于题目给的BNF,其实就是告诉你(用一种很正式,很严谨,很。。。的方式)方程式的结构,有好几层,具体来说看看我代码里面ParseN
的函数就知道了。前面的几层,即等号,加号的处理,都很简单。难度在于单个化学式的括号可以嵌套,这就用到上面说的栈了,具体写法还是很像JSON的。不要担心内存,用对的数据结构把问题解决了就行。大模拟一般卡不到时间复杂度和空间复杂度的,卡的是设计。
还有就是把后缀数字记法,即H2O的2,用一个函数来实现,这样会方便一些,因为代码有两个地方用到,并且不能把公共部分提出来,只好单独做成函数了。这里和JSON那题字符串抠取也是类似的。如果发现一些代码可以抽出来,并且有好处没坏处,就果断抽出来,这样可以节省时间。
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cassert>
#include <stack>
#include <map>
using namespace std;
typedef map<string,int> Map;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
const int MAXL=1005;
char str[MAXL];
Map lmp, rmp;
typedef Map::iterator It;
void Print(int l, int r) {
while (l<=r) {
putchar(str[l]);
++l;
}
puts("");
}
void PM(Map& mp) {
for (It it=mp.begin();it!=mp.end();++it) {
printf("%s %d\n", it->first.c_str(), it->second);
}
}
void Merge(Map& a, Map& b, int n) {
// a+=b*n;
for (It it=b.begin();it!=b.end();++it) {
a[it->first] += it->second*n;
}
}
int FindNum(int& i, int right) {
if (i<=right && isdigit(str[i])) {
int n=0;
while (i<=right && isdigit(str[i])) {
n=n*10+(str[i]-'0');
++i;
}
return n;
}
return 1;
}
Map Parse4(int left, int right) {
/*
把一个化学式看做一些项的序列。
*/
stack<Map*> stk;
// 顶层列表计数。
Map* now=new Map;
int i=left;
while (i<=right) {
if (str[i]=='(') {
// 开始新的列表的计数,保存旧的map。
stk.push(now);
now=new Map;
++i;
} else if (str[i]==')') {
// 当前列表计数完毕。
++i;
if (stk.empty()) {
// 当前的列表就是顶级。
break;
} else {
int n=FindNum(i, right);
Map* fa=stk.top();
stk.pop();
// 把当前列表合并到上一级列表。
Merge(*fa, *now, n);
delete now;
now=fa;
}
} else {
// 简单元素,即Na或者H。
assert(isupper(str[i]));
string elem;
elem.push_back(str[i]);
++i;
if (i<=right && islower(str[i])) {
elem.push_back(str[i]);
++i;
}
int n=FindNum(i, right);
(*now)[elem] += n;
}
}
// Print(left, right);
// PM(*now);
return *now;
}
void Parse3(Map& mp, int left, int right) {
// 处理单个化学式。
int coef=1;
if (isdigit(str[left])) {
coef=0;
while (left <= right && isdigit(str[left])) {
coef=coef*10+(str[left]-'0');
++left;
}
}
Map tot=Parse4(left, right);
Merge(mp, tot, coef);
}
void Parse2(Map& mp, int left, int right) {
// 分割成+分割的多个公式。
int i=left;
while (i <= right) {
int j=i;
while (j<=right && str[j]!='+') {
++j;
}
Parse3(mp, i, j-1);
i=j+1;
}
}
bool Parse(int left, int right) {
// 分成=分割的两个公式。
lmp.clear();
rmp.clear();
int i=left;
while (i<=right && str[i] != '=') {
++i;
}
Parse2(lmp, left, i-1);
Parse2(rmp, i+1, right);
#if 0
PM(lmp);
puts("");
PM(rmp);
#endif
return lmp == rmp;
}
int main(int argc, char** argv) {
int N;
scanf("%d",&N);
getchar();
while (N--) {
fgets(str, sizeof(str), stdin);
bool ans=Parse(0, strlen(str)-2);
puts(ans?"Y":"N");
}
return 0;
}