前言
提示:以下是本篇文章正文内容,编程语言为Java
一、题目描述
给定一个用字符串展示的化学公式,计算每种元素的个数。
规则如下:
- 元素命名采用驼峰命名,例如 Mg
- () 代表内部的基团,代表阴离子团
- [] 代表模内部链节通过化学键的连接,并聚合
例如:H2O => H2O1 Mg(OH)2 => H2Mg1O2
示例 1:
输入:
- 化学公式的字符串表达式,例如:K4[ON(SO3)2]2 。
输出:
- 元素名称及个数:K4N2O14S4,并且按照元素名称升序排列。
链接:化学公式解析
二、解题思路
该题和 字符串解码 相似,我们把输入的字符串反转,其格式和字符串解码就非常相似了。本题相对比较复杂,步骤更多,详细可以看注释。
三、示例代码
import java.util.*;
public class Solution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 反转之后的字符串长这样: 2]2)3OS(NO[4K
String s = new StringBuffer(sc.nextLine()).reverse().toString();
Deque<String> st = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
//反转了,所以不是 左括号 就直接入栈
if (c != '[' && c != '(') {
st.push(Character.toString(c));
}
//遇到 左括号 就要 解码 了
else {
//首先处理字母
StringBuffer letterStr = new StringBuffer();
char tmp = st.peek().charAt(0);
while (tmp != ')' && tmp != ']') {
letterStr.append(st.pop());
tmp = st.peek().charAt(0);
}
//处理右括号
st.pop();
//特殊情况 )O( , 所以要判断栈是否为空,若为空说明数字为1,否则就要处理数字了
if (!st.isEmpty()) {
//栈非空,需要处理数字
tmp = st.peek().charAt(0);
StringBuffer numStr = new StringBuffer();
while (Character.isDigit(tmp)) {
numStr.append(st.pop());
if (st.isEmpty()) break;
tmp = st.peek().charAt(0);
}
int cnt = Integer.parseInt(numStr.toString());
StringBuffer newStr = new StringBuffer();
//将字符串 letterStr 重复 cnt 次 得到新字符串
while (cnt > 0) {
newStr.append(letterStr);
--cnt;
}
//将新字符串放入栈
st.push(newStr.toString());
}
//以上和字符串解码步骤基本相同
//栈为空,说明数字为1,直接将 letterStr 放入栈
else {
st.push(letterStr.toString());
}
}
}
StringBuffer ansStr = new StringBuffer();
Map<String, Integer> map = new TreeMap();
//将栈内元素转为字符串 K4ONSO3SO3ONSO3SO3
while (!st.isEmpty()) {
ansStr.append(st.pop());
}
String str = ansStr.toString();
//统计字符串 K4ONSO3SO3ONSO3SO3 中每种元素的个数
for (int i = 0; i < str.length(); i++) {
//若不是数字,直接加入map
if (!Character.isDigit(str.charAt(i))) {
String s1 = str.charAt(i) + "";
String s2 = "";
//考虑特殊情况 Mg
if (i < str.length() - 1 && Character.isLowerCase(str.charAt(i + 1))) {
s2 = str.charAt(i + 1) + "";
i++;
}
map.put(s1 + s2, map.getOrDefault(s1 + s2, 0) + 1);
continue;
}
//是数字,需要先得到数字
StringBuffer num = new StringBuffer();
//记录第一个数字的前一个字母的位置
int cur = i - 1;
//处理数字
while (i < str.length() && Character.isDigit(str.charAt(i))) {
num.append(str.charAt(i));
i++;
}
i--;
int cnt1 = Integer.parseInt(num.toString());
//获取和数字配对的元素
StringBuffer sb = new StringBuffer();
for (int j = cur; j >= 0; j--) {
sb.insert(0, str.charAt(j));
if (Character.isUpperCase(str.charAt(j))) break;
}
//将元素个数插入map,因为前面已经+1了,所以个数要先-1
map.put(sb.toString(), map.getOrDefault(sb.toString(), 0) + cnt1 - 1);
}
//处理成正确的格式
StringBuffer ans = new StringBuffer();
map.forEach((k, v) -> ans.append("" + k + v));
System.out.println(ans.toString());
}
}