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