Loading...

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

在这里插入图片描述

求解思路

采用递归 + TreeMap 的方案:

用递归处理从当前位置到右括号或字符串末尾的内容,遇到大写字母或左括号时,将之前的内容结算到总表中;而TreeMap能够自动保证字典序,便于最终输出。

完整代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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;  // 没有数字默认为1
            
            if (name.length() > 0) {
                // 处理单个原子:如 H2 中的 H
                String 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);
                }
            }
        }
    }
}

如果觉得有帮助,欢迎点赞、关注、转发~

最后更新于 2026-04-05 17:35:33
Code Road Record