Python-CCF:20191203 化学方程式(使用正则表达式re库)

Judith ·
更新时间:2024-09-21
· 786 次阅读

题目链接
为了降低难度。以下分别为,只考虑大小写字母和等号的方程组、加入数字、加入括号(非嵌套)

import re def cnt(string: str): pattern = r"[A-Z][a-z]*" #匹配元素 elements = re.findall(pattern, string) count = {} # 统计元素词频 for item in elements: if item in count: count[item] += 1 else: count[item] = 1 return count if __name__ == '__main__': n = int(input()) for i in range(n): equation = input() left, right = equation.split("=") # 统计等式左边的元素个数 leftCnt = cnt(left) # 统计等式右边的元素个数 rightCnt = cnt(right) # 输入比较结果 if leftCnt == rightCnt: print("Y") else: print("N")

note: 这道题继续使用正则进行处理,不考虑括号情况可以轻松刷到60分

【更新60分简洁代码版本】

import re # 统计形如2CaCl2、CuCl2等单个项的元素个数 def cnt_word(word: str): # word = 2CaCl2 pattern = r"(\d*)(\w+)" # 返回的结果对象中obj,使用obj[groupId](1或2,注意是1开始,不是0)访问系数或系数后面的内容 # 例如对于2CaCl2,将返回 "2","CaCl2" groups = re.match(pattern, word) # d # 1、提取系数和非系数内容, 后面会使用系数将元素数量翻倍 coefficient = groups[1] if coefficient == "": coefficient = 1 else: coefficient = int(coefficient) # 2、提取非系数内容 none_coe = groups[2] # 2.1 提取所有的元素项目,例如 Ca 、Cl2 pattern = r"([A-Z][a-z]*)(\d*)" # 比如CaNo3H3,将返回[('Ca', ''), ('No', '3'), ('H', '3')] ele_list = re.findall(pattern, none_coe) t_cnt = {} for item in ele_list: # 取得对于的元素和下标 element = item[0] ele_cnt = int(item[1]) if item[1] != "" else 1 # 存入字典 if element in t_cnt: t_cnt[element] += ele_cnt else: t_cnt[element] = ele_cnt # 3.将系数并入结果 for key in t_cnt: t_cnt[key] *= coefficient return t_cnt # 返回等式某一边的元素统计 def cnt_one_side(one_side: str): side_cnt = {} for word in one_side.split("+"): t_cnt = cnt_word(word) for ele in t_cnt: if ele in side_cnt: side_cnt[ele] += t_cnt[ele] else: side_cnt[ele] = t_cnt[ele] return side_cnt if __name__ == '__main__': n = int(input()) for i in range(n): equation = input() left, right = equation.split("=") # 统计等式两边的元素个数 left_cnt = cnt_one_side(left) right_cnt = cnt_one_side(right) # 输出结果 if left_cnt == right_cnt: print("Y") else: print("N") import re # 分离系数和非系数部分,例如,对于输入4AuCl2(ON)2 ---> 4 和 AuCl2(ON)2 def sep_coe(item): pattern = r"(\d*)" # 返回的结果对象中obj,使用obj[groupId](1或2,注意是1开始,不是0)访问系数或系数后面的内容 # 例如对于2CaCl2,将返回 "2","CaCl2" groups = re.match(pattern, item) # d # 1、提取系数和非系数内容, 后面会使用系数将元素数量翻倍 coefficient = groups[1] if coefficient == "": coefficient = 1 else: coefficient = int(coefficient) # 2、提取非系数内容 pattern = r"\d*(.+)" groups = re.findall(pattern, item) none_coe = groups[0] # 返回系数和非系数部分 return coefficient, none_coe # 统计简单项的元素个数例如。 H2 Cl2 PO3 等等不含有系数和括号的项 def cnt_simple_ele(ele: str): pattern = r"([A-Z][a-z]*)(\d*)" # 比如CaNo3H3,将返回[('Ca', ''), ('No', '3'), ('H', '3')] ele_list = re.findall(pattern, ele) t_cnt = {} for item in ele_list: # 取得对于的元素和下标 element = item[0] ele_cnt = int(item[1]) if item[1] != "" else 1 # 存入字典 if element in t_cnt: t_cnt[element] += ele_cnt else: t_cnt[element] = ele_cnt return t_cnt # 统计含有括号的项的元素个数。例如Au(CN)3(OH)2中的(CN)3和(OH)2 def cnt_parentheses_ele(par_ele: str): p = r"\((\w+)\)(\d*)" res_list = re.findall(p, par_ele) # 经过正则表达式后:会被分割成两部分:括号里的内容和括号右边的数字 cnt = {} for item in res_list: t_cnt = {} # 提取括号里内容和括号外数字 simple_ele = item[0] subscript = int(item[1]) if item[1] != "" else 1 # 统计括号内元素的数量,再乘以括号外的数字 cnt_simple = cnt_simple_ele(simple_ele) for ele in cnt_simple: cnt_simple[ele] *= subscript for ele in cnt_simple: if cnt.get(ele) is not None: cnt[ele] += cnt_simple[ele] else: cnt[ele] = cnt_simple[ele] return cnt # 统计由 "+" 分割的项目元素个数.例如 4AuCl2(ON)2 def cnt_word(word: str): # word = 2CaCl2 cnt = {} # 1.获取系数和非系数部分 coefficient, none_coe = sep_coe(word) # 对系数后的内容分两种情况提取。 # 2.1 提取所有的元素项目(不带括号)的元素个数统计,例如 Ca 、Cl2 patt = r"\((\w+)\)(\d*)" simple_str = re.sub(patt, "", none_coe) cnt_simple = cnt_simple_ele(simple_str) # 2.2 提取带括号的,输入(ON)2 -----> ON , 3 cnt_parentheses = cnt_parentheses_ele(none_coe) # 3.合并2.1和2.2的结果 for ele in cnt_simple: if ele in cnt: cnt[ele] += cnt_simple[ele] * coefficient else: cnt[ele] = cnt_simple[ele] * coefficient for ele in cnt_parentheses: if ele in cnt: cnt[ele] += cnt_parentheses[ele] * coefficient else: cnt[ele] = cnt_parentheses[ele] * coefficient return cnt # 返回等式某一边的元素统计 def cnt_one_side(one_side: str): side_cnt = {} for word in one_side.split("+"): t_cnt = cnt_word(word) for ele in t_cnt: if ele in side_cnt: side_cnt[ele] += t_cnt[ele] else: side_cnt[ele] = t_cnt[ele] return side_cnt if __name__ == '__main__': n = int(input()) for i in range(n): equation = input() left, right = equation.split("=") # 统计等式两边的元素个数 left_cnt = cnt_one_side(left) right_cnt = cnt_one_side(right) # 输出结果 if left_cnt == right_cnt: print("Y") else: print("N")

体会:
虽然只拿了80分。没有考虑嵌套。但是我相信聪明的你们,利用我封装好的函数,对程序修改以下便能获得满分。
二八原则:随着考虑情况的越来越多。代码从最开始的30行–>60行–>120行。翻倍增长。后面的20%可以要占用你80%的时间和精力。
本文充分利用的正则表达式进行分割。提取数字和元素。当然自己实现不会太难。但代码会更长

【更新:某大佬的写法】: # coding=utf-8 # Copyright (c) 2020. ItsukiFujii. import re import collections list_formula = [] list_terms_left = [] list_terms_right = [] list_res = [] dict_atoms_left = collections.defaultdict(int) dict_atoms_right = collections.defaultdict(int) # calculate each single compound def fk_it(formula): stack = list() stack.append(collections.Counter()) found = re.findall(r'([A-Z][a-z]?)(\d*)|(\()|(\))(\d*)', formula) for name, n_atom, open_paren, close_paren, coef in found: if name: stack[-1][name] += int(n_atom or 1) if open_paren: stack.append(collections.Counter()) if close_paren: top = stack.pop() for key in top.keys(): top[key] *= int(coef or 1) stack[-1] += top #合并两个Counter return dict(stack.pop()) # handle each single equation def handle_equation(terms_all, dict_atoms): for term in terms_all: coef_term = re.match(r'\d+', term) coef_term = 1 if coef_term is None else int(coef_term.group()) tmp = fk_it(term) for key in tmp.keys(): tmp[key] *= coef_term for k, v in tmp.items(): if str(dict_atoms.keys()).find(k) != -1: dict_atoms[k] += v else: dict_atoms.update({k: v}) if __name__ == '__main__': num_of_formula = int(input()) for i in range(num_of_formula): list_formula.append(input()) list_res.append('Y') # split the equations for item in list_formula: list_terms_left.append(item[:item.find('=')].split('+')) list_terms_right.append(item[item.find('=') + 1:].split('+')) # judge whether the number of atom on both side is equal cntr_tmp = 0 for terms_all_left, terms_all_right in zip(list_terms_left, list_terms_right): dict_atoms_left.clear() dict_atoms_right.clear() # left part handle_equation(terms_all_left, dict_atoms_left) # right part handle_equation(terms_all_right, dict_atoms_right) # compare the numbers if dict_atoms_left != dict_atoms_right: list_res[cntr_tmp] = 'N' # print(dict_atoms_left, dict_atoms_right, list_res[cntr_tmp]) # for debug print(list_res[cntr_tmp]) cntr_tmp += 1
作者:TransPlus



化学 化学方程式 正则 Python

需要 登录 后方可回复, 如果你还没有账号请 注册新账号