化学式字符串解析:一道考验递归功底的经典题

求解思路
采用递归 + TreeMap 的方案:
用递归处理从当前位置到右括号或字符串末尾的内容,遇到大写字母或左括号时,将之前的内容结算到总表中;而TreeMap能够自动保证字典序,便于最终输出。
完整代码实现
public class Solution {// 全局变量,记录递归处理到的位置public static int where;public static String countOfAtoms(String str) {where = 0;// 递归解析整个字符串TreeMap<String, Integer> map = f(str.toCharArray(), 0);// 构建输出结果StringBuilder ans = new StringBuilder();for (String key : map.keySet()) {ans.append(key);int cnt = map.get(key);if (cnt > 1) { // 数量为1时不输出数字ans.append(cnt);}}return ans.toString();}public static TreeMap<String, Integer> f(char[] s, int i) {TreeMap<String, Integer> ans = new TreeMap<>(); // 当前层级的总表StringBuilder name = new StringBuilder(); // 当前正在收集的原子名称TreeMap<String, Integer> pre = null; // 上一个括号的统计结果int cnt = 0; // 当前收集的倍数while (i < s.length && s[i] != ')') {// 遇到大写字母或左括号:触发结算if (s[i] >= 'A' && s[i] <= 'Z' || s[i] == '(') {fill(ans, name, pre, cnt); // 将之前的内容加入总表// 重置状态name.setLength(0);pre = null;cnt = 0;if (s[i] >= 'A' && s[i] <= 'Z') {// 收集新的原子名称(以大写字母开头)name.append(s[i++]);} else {// 遇到左括号:递归处理括号内容pre = f(s, i + 1);i = where + 1; // 跳过递归处理过的部分}} // 小写字母:追加到原子名称else if (s[i] >= 'a' && s[i] <= 'z') {name.append(s[i++]);} // 数字:累加倍数else {cnt = cnt * 10 + s[i++] - '0';}}// 处理最后一部分内容fill(ans, name, pre, cnt);where = i; // 更新全局位置,告知上层递归处理到哪里return ans;}public static void fill(TreeMap<String, Integer> ans, StringBuilder name, TreeMap<String, Integer> pre, int cnt) {if (name.length() > 0 || pre != null) {cnt = cnt == 0 ? 1 : cnt; // 没有数字默认为1if (name.length() > 0) {// 处理单个原子:如 H2 中的 HString key = name.toString();ans.put(key, ans.getOrDefault(key, 0) + cnt);} else {// 处理括号结果:如 (OH)2 中的 OH 表for (String key : pre.keySet()) {ans.put(key, ans.getOrDefault(key, 0) + pre.get(key) * cnt);}}}}
}
如果觉得有帮助,欢迎点赞、关注、转发~
