CCF 化学方程式 201912-3 100分 46ms

点击前往试题目录:https://blog.csdn.net/best335/article/details/99550556

题目描述

在这里插入图片描述

解题思路

你需要熟读试题前边有黑点并且加粗的部分,题目说的很明白。

定义:

  • &’:表示左右两部分紧密连接
  • |’ :表示左边部分或者右边部分
  • 优先级:’()’ > ‘&’ > ‘|’ > ‘+’ > ‘=’

公式:

  • 表达式=表达式
  • 系数1&化学式1+系数2&化学式2+…系数n&化学式n=系数1&化学式1+系数2&化学式2+…系数m&化学式m
  • 化学式=项1&系数1&项2&系数2&…项n&系数n
  • =元素|(化学式)
  • 元素=大写字母|大写字母&小写字母

题意额外信息:

  • 放心使用int不会溢出。
  • 放心处理方程式,没有其他符号,且一定符合描述规范。

注意:那个巴斯克不要看,如果看不懂的话,不要纠结。

通过以上分析,我们知道循环嵌套的位置只有化学式

按照正常逻辑我们先计算出左边所有元素及其数量,再计算右边所有元素及其数量。
对于这两边,我们以系数&化学式为最小单位进行处理,若化学式有嵌套,那么我们把之前计算的系数与之后的系数乘积传过去就OK。
选用好合适的数据结构减少查询时间复杂度和插入时间复杂度,这里我们选择unordered_map<string,int>,来存储元素及其对应的数量,不管查询还是插入时间复杂度都为O(1)。
这样等式两边各维护一个unordered_map容器,待两边都计算完成后,比较容器内的元素是否完全相同,若相同输出一行‘Y’,否则输出一行‘N’

那么我们开始编码了:
友情提示:代码底部有测试数据

#include<iostream>
#include<unordered_map>
using namespace std;

//#define DEBUG 1  //是否调试,若调试取消注释
//#define LOG 1    //是否打印log,若打印取消注释

//获取从s字符串开始的数字,并更新引用的索引指针startIndex,如果没有数字则返回默认值defaultValue
//注意不需要处理负数
int getNumberOrDefault(const string& s, int& startIndex, const int& defaultValue){
	int n = s.size(),retValue = 0;
	for(;startIndex<n;++startIndex){
		if(!isdigit(s[startIndex])) break;
		retValue = retValue*10 +(s[startIndex]-'0');
	}
	return retValue==0 ? defaultValue : retValue;
}
//处理 系数&化学式
void procExpressionUnit(unordered_map<string,int> &m, const string &s, const int& unit){
	//查找容器中是否插入过某元素
	unordered_map<string,int>::iterator it;

	int i=0,n=s.size();//循环起点和终点
	int base = unit * getNumberOrDefault(s,i,1);//当前化学式的系数
	int pNumber,pElement = i;//指向数字起始位置的指针和指向元素起始位置的指针

	while(i<n){
		if(isalpha(s[i])){//处理元素
			pElement=i;//记录元素起始位置
			if(i+1<n&&islower(s[1+i])) ++i;

			pNumber = ++i;//记录系数起始位置
			string strElement = s.substr(pElement,i-pElement);

			int entry_base = base;//该元素待计算的系数
			if(pNumber != n&&isdigit(s[i])){//如果有系数
				entry_base = base * getNumberOrDefault(s,i,1);//更新该元素系数 更新i
			}
			//以下将元素个数累加到容器中
			if((it=m.find(strElement))==m.end()){
				m.insert(pair<string,int>(strElement,entry_base));
			}
			else{
				it->second += entry_base;
			}
			continue;
		}
		if(s[i]=='('){//处理化学式
			//以下查找匹配的‘(’位置
			int countLeft = 1;
			pElement = ++i;
			for(;i<n;++i){
				if(s[i]=='(') ++countLeft;
				else if(s[i]==')') --countLeft;
				if(countLeft==0) break; 
			}
			
			string strElement = s.substr(pElement,i-pElement);//化学式 也是项 紧跟着要处理其后的数字
			pNumber = ++i;//记录项后边数字的位置
			procExpressionUnit(m,strElement,base*getNumberOrDefault(s,pNumber,1));//处理这个化学式 并更新项的系数
			if(pNumber<n)//如果还有要处理的项
				procExpressionUnit(m,s.substr(pNumber,n-pNumber),base);
			return;
		}
	}
}
//处理表达式将表达式拆分成最小单位为:  系数&化学式
void procExpression(unordered_map<string,int> &m, const string &s){
	string sf = s+"+";
	int lastIndex = 0,currIndex = 0;
	while((currIndex = sf.find('+',currIndex))!=string::npos){
		procExpressionUnit(m,sf.substr(lastIndex,currIndex-lastIndex),1);
		lastIndex = ++currIndex;
	}
}
//判断字符串s是否是合法的化学方程式
bool isValidFormula(const string &s){
	//声明左右两边表达式容器:unordered_map<元素名,元素个数>
	unordered_map<string,int> leftExpression;
	unordered_map<string,int> rightExpression;
	//拆分并处理两边表达式
	int idx = s.find('=');
	procExpression(leftExpression,s.substr(0,idx));
	procExpression(rightExpression,s.substr(idx+1,s.size()-idx-1));
	//打印左右两边元素及其个数信息
	#ifdef LOG
	cout<<"Left"<<endl;
	for(unordered_map<string,int>::iterator itL = leftExpression.begin(),itR;itL!=leftExpression.end(); ++itL){
		cout<<itL->first<<"->"<<itL->second<<endl; 
	}
	cout<<"Right"<<endl;
	for(unordered_map<string,int>::iterator itR = rightExpression.begin();itR!=rightExpression.end(); ++itR){
		cout<<itR->first<<"->"<<itR->second<<endl; 
	}
	#endif
	//开始判断两边表达式是否相等
	if(leftExpression.size()!=rightExpression.size()) return false;//第一层过滤,数量不等一定不合法
	for(unordered_map<string,int>::iterator itL = leftExpression.begin(),itR;itL!=leftExpression.end(); ++itL){//第二层过滤
		itR = rightExpression.find(itL->first);
		if(itR == rightExpression.end()) return false;//左边元素在右边元素中不存在
		if(itR->second!=itL->second) return false;//左边元素与右边元素数量不相等
	}
	return true;//不存在其他情况使两边表达式不等
}

#ifdef DEBUG
	#include<fstream>
#endif

int main(){
	#ifdef DEBUG
		ifstream cin("input.txt");
	#endif

	int n;
	string s;
	cin>>n,getline(cin,s);//读取n,并去除这行的回车
	for(int i=0;i<n;++i){//不断读取行数据,做判断
		getline(cin,s);
		if(isValidFormula(s))
			cout<<"Y"<<endl;
		else
			cout<<"N"<<endl;
	}
	return 0;
}
/*
试题测试样例:NNNYYYYYYYY
11
H2+O2=H2O
Cu+As=Cs+Au
H2+Cl2=2NaCl
2H2+O2=2H2O
H2+Cl2=2HCl
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
4Au+8NaCN+2H2O+O2=4Na(Au(CN)2)+4NaOH

自测测试样例:YYYYYYYYYYYYY 
13
AAAAAAA=AAAAAAA
AaAaAaAa=AaAaAaAa
ABCDEFG=GFEDCBA
AaABbBCcCDdDEeEFfFGgG=GgGFfFEeEDdDCcCBbBAaA
2AAAAAA=3AAAA
2AaAAaAAaAAaAAaAAaA=3AaAAaAAaAAaA
(((((((((((((A)))))))))))))=(((((((((((((A)))))))))))))
(((((((((((((Aa)))))))))))))=(((((((((((((Aa)))))))))))))
(((((((((((((AaA)))))))))))))=(((((((((((((AAa)))))))))))))
(((((((((((((A)13)12)11)10)9)8)7)6)5)4)3)2)1=1(2(3(4(5(6(7(8(9(10(11(12(13(A)))))))))))))
(((((((((((((A)1)12)3)10)5)8)7)6)9)4)11)2)13=1(12(3(10(5(8(7(6(9(4(11(2(13(A)))))))))))))
(A)(A)(A)(A)(A)(A)(A)=(A)(A)(A)(A)(A)(A)(A)
3A(A(A(A(A(((A))B2)2AB)2A)2A)2A)2A=3AA+6AA+12AA+24AA+48AAB+96ABB
*/
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值