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

7-5 Easy chemistry

In this question, you need to write a simple program to determine if the given chemical equation is balanced. Balanced means that the amount of elements on both sides of the “=” sign is the same.
H 2 O + C O 2 = H 2 C O 3 H​2​​O+CO​2​​=H​2​​CO​3​​ H2O+CO2=H2CO3

We guarantees that each chemical equation satisfies the basic rules.

If you don’t know the basic rules of chemical equation writing, you can refer to the following conditions:

  • The chemical equation contains exactly one “=” sign.
  • For every chemical element, the first letter of each element is uppercase, and the rest of the letters are lowercase.
  • The number at the end of the element represents the amount of this element needed, If there is no number, it means only one element is needed.
  • Every chemical object may contain several chemical elements like NaCl.
    The number at the beginning of every chemical object represents the number of this object, If there is no number, it means only one object is needed.
  • Every chemical object will be connected by “+” sign.
  • Chemical reaction is considered to rearrange the atoms after they are broken up

Input

The first line contains a single integer T(1≤T≤100), indicating the number of test cases.

In the next T lines, each line contains a string S(1≤∣S∣≤100), representing a Chemical equation.

It is guaranteed that S only contains uppercase letters, lowercase letters, “=” sign and “+” sign, the sum of the amount of all elements will not exceed 2147483647.
Output

If the given chemical equation is balanced, please output “Easy!”, otherwise output “Hard!” (Without quotes).
Sample Input

3
H2O+CO2=H2CO3
2NaHCO3=Na2CO3+H2O+CO2
3Cu+8HNO3=3CuNO3+2NO+4H2O

Sample Output

Easy!
Easy!
Hard!

作者
tt
单位
浙江大学城市学院
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB

题意 : 给定一个化学方程式字符串,判断左右两边原子个数是否相等

  • 字符串有且仅有一个等于号 " = " "=" "="
  • 等号两边是 " A + B + . . . N " "物质A+物质B+...物质N" "A+B+...N"
  • i 物质_i i由大小写字母和数字组成,形如 2 N a C O 3 2NaCO3 2NaCO3
  • 对于 2 N a C O 3 2NaCO3 2NaCO3,发现个数为: N a = 2 1 , C = 2 1 , O = 2 3 Na=2*1,C=2*1,O=2*3 Na=21,C=21,O=23
  • 多组样例


这题看似别扭,但其实很简单,
2 N a C O 3 2NaCO3 2NaCO3可以看成是 [ n u m L ] [ N a m e 1 ] [ n u m R 1 ] [ N a m e 2 ] [ n u m R 2 ] . . . . [ N a m e N ] [ n u m R N ] [numL][Name_1][numR_1][Name_2][numR_2]....[Name_N][numR_N] [numL][Name1][numR1][Name2][numR2]....[NameN][numRN]
用两个map统计等号左右两边的原子个数,最后比较两个map是否全等
把状态机画出来,再对着状态机写代码即可 1 A 1A 1A (调代码调了一上午)

状态图如下:
代码如下(一大堆又臭又长的 i f e l s e if-else ifelse):

#define debug
#ifdef debug
#include <time.h>
#include "/home/majiao/mb.h"
#endif

#include <iostream>
#include <algorithm>
#include <vector>
#include <string.h>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <math.h>

#define MAXN ((int)1e5+7)
#define ll long long int
#define INF (0x7f7f7f7f)
#define fori(lef, rig) for(int i=lef; i<=rig; i++)
#define forj(lef, rig) for(int j=lef; j<=rig; j++)
#define fork(lef, rig) for(int k=lef; k<=rig; k++)
#define QAQ (0)

using namespace std;

#ifdef debug
#define show(x...) \ do { \ cout << "\033[31;1m " << #x << " -> "; \ err(x); \ } while (0)

void err() { cout << "\033[39;0m" << endl; }
template<typename T, typename... A>
void err(T a, A... x) { cout << a << ' '; err(x...); }
#endif

#ifndef debug
namespace FIO {
	template <typename T>
	void read(T& x) {
		int f = 1; x = 0;
		char ch = getchar();

		while (ch < '0' || ch > '9') 
			{ if (ch == '-') f = -1; ch = getchar(); }
		while (ch >= '0' && ch <= '9') 
			{ x = x * 10 + ch - '0'; ch = getchar(); }
		x *= f;
	}
};
using namespace FIO;
#endif


int n, m, Q, K;
char buf[MAXN];
map<string, int> mp1, mp2; //map1代表等号左边的原子个数表
                           //map2代表等号右边

#define ch (buf[i])
#define Big(x) (x>='A' && x<='Z')
#define Small(x) (x>='a' && x<='z')
#define NUML (numL ? numL : 1)
#define NUMR (numR ? numR : 1)
#define Num(x) (x>='0' && x<='9')

#define mp (*ptr)

//to0是跳回状态0
#define to0 {name.clear();\ status = numL = numR = 0;}

void slove() {
	map<string, int>* ptr = &mp1; //初始状态为左边的map
	int status = 0, numL = 0, numR = 0;
	string name = "", ptag = "map1 : ";
	//状态 : 0-初始状态, 1-读物质名字name,
	// 2-读原子个数numR, 3-读物质个数numL
	for(int i=0; buf[i]; ) { //注意这里不要i++
		if(status == 0) { //起始状态
			if(Big(ch)) { //遇到大写 转化为1状态,读name
				status = 1;
			} else if(Num(ch)) { //遇到数字 转化为3状态, 读物质个数numL
				status = 3;
			}
		} else if(status == 1) { //1状态是读取名字
			if(Big(ch)) {
				if(!name.empty()) { //更新上一个名字到map
					mp[name] += (NUML * NUMR);
					name.clear();
				}
				name.push_back(ch); //加入
				i ++;
			} else if(Num(ch)) { //遇到数字,说明要去读numR,原子个数
				status = 2;
			} else if(Small(ch)) { //小写也是上一个name的一部分
				name.push_back(ch);
				i ++;
			} else if(ch == '=') { //等号要切换map并回到初始状态
				mp[name] += (NUML * NUMR); //当然要先更新上一个name
				ptr = &mp2; //切换到右边的map
				to0;
				i ++;
			} else if(ch == '+') { //遇到+要读下一个"2NaCO3"
				mp[name] += (NUML * NUMR);
				to0;
				i ++;
			}
		} else if(status == 2) { //状态2要读原子个数numR
			if(Big(ch)) { //遇到大写,说明当前原子的个数numR读完了,跳回1继续读name
				mp[name] += (NUML * NUMR); //更新0
				name.clear();
				numR = 0;
				status = 1;
			} else if(Num(ch)) { 
				numR = numR * 10 + (ch - '0');
				i ++;
			} else if(Small(ch)) {

			} else if(ch == '=') { //等号同1里的等号
				mp[name] += (NUML * NUMR);
				ptr = &mp2;
				to0;
				i ++;
			} else if(ch == '+') { //加号同1里的加号
				mp[name] += (NUML * NUMR);
				to0;
				i ++;
			}
		} else if(status == 3) { //状态3是读总物质个数numL
			if(Big(ch)) { //遇到大写说明当前numL读完了,跳去读name
				mp[name] += (NUML * NUMR);
				numR = 0;
				name.clear();
				status = 1; //改成status = 0也可
			} else if(Num(ch)) {
				numL = numL * 10 + (ch - '0');
				i ++;
			} else if(Small(ch)) {

			} else if(ch == '=') {

			} else if(ch == '+') {

			}
		}
	}
#if 0
	mp1.erase(""), mp2.erase("");
	for(auto it : mp1)
		cout << "[" << it.first << "," << it.second << "], ";
	cout << endl;
	for(auto it : mp2)
		cout << "[" << it.first << "," << it.second << "], ";
	cout << endl;
#else
   mp1.erase(""), mp2.erase(""); //删掉两个map的空串
   if(mp1.size() != mp2.size()) //两边原子种类数量不同,就no
	   printf("Hard!\n");
   else { //比较每个原子个数是否相同
	   bool ok = true;
	   for(auto it=mp1.begin(), it2=mp2.begin(); it!=mp1.end(); it++, it2++) {
		   if(it->first!=it2->first || it->second!=it2->second)
			   ok = false;
	   }
	   printf("%s\n", !ok ? "Hard!" : "Easy!");
   }
#endif
}

int main() {
#ifdef debug
	freopen("test", "r", stdin);
	clock_t stime = clock();
#endif
	scanf("%d ", &Q);
	while(Q--) {
		mp1.clear(), mp2.clear();
		scanf("%s ", buf);
		n = strlen(buf);
		buf[n] = '+'; //字符串屁股后加入一个+,就不用单独处理字符串末尾了
		buf[n+1] = 0;
		slove();
	}





#ifdef debug
	clock_t etime = clock();
	printf("rum time: %lf 秒\n",(double) (etime-stime)/CLOCKS_PER_SEC);
#endif 
	return 0;
}




全部评论

相关推荐

CVTE校招内推:可以试试我们这,硬件还没招满
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
正在热议
更多
# 听劝,这个简历怎么改 #
14116次浏览 183人参与
# 面试被问“你的缺点是什么?”怎么答 #
6486次浏览 100人参与
# 水滴春招 #
16614次浏览 361人参与
# 入职第四天,心情怎么样 #
11331次浏览 63人参与
# 租房找室友 #
8035次浏览 53人参与
# 读研or工作,哪个性价比更高? #
26176次浏览 356人参与
# 职场新人生存指南 #
199273次浏览 5510人参与
# 参加完秋招的机械人,还参加春招吗? #
27030次浏览 276人参与
# 文科生还参加今年的春招吗 #
4122次浏览 31人参与
# 简历无回复,你会继续海投还是优化再投? #
48634次浏览 561人参与
# 你见过最离谱的招聘要求是什么? #
144724次浏览 829人参与
# 如果重来一次你还会读研吗 #
155720次浏览 1706人参与
# 机械人选offer,最看重什么? #
69078次浏览 449人参与
# 选择和努力,哪个更重要? #
44330次浏览 494人参与
# 如果再来一次,你还会学硬件吗 #
103653次浏览 1245人参与
# 如果你有一天可以担任公司的CEO,你会做哪三件事? #
20529次浏览 414人参与
# 招聘要求与实际实习内容不符怎么办 #
46804次浏览 494人参与
# 22届毕业,是读研还是拿外包offer先苟着 #
4652次浏览 27人参与
# 你们的毕业论文什么进度了 #
901330次浏览 8961人参与
# 软开人,你觉得应届生多少薪资才算合理? #
81380次浏览 496人参与
# 国企还是互联网,你怎么选? #
109200次浏览 853人参与
牛客网
牛客企业服务