找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4465

积分

0

好友

590

主题
发表于 1 小时前 | 查看: 1| 回复: 0

“把工资卡交出来,我帮你管钱。” 这话一出来,很多人可能就觉得味道不对了。谈恋爱毕竟不是公司财务外包,更不是先把人谈到手,再把账户也一锅端。

女朋友管钱问题社交媒体截图

评论区有网友说得挺尖锐:这不像是在谈理财,倒像是要先拿下经营权。还有人更直接,没结婚就要全盘接管,下一步是不是连点个外卖都得走审批流程?听着像段子,但仔细一想,这事儿其实一点也不好笑。

钱这个东西,最怕的其实不是少,而是边界感消失。两个人愿意一起做预算、存共同基金、商量大额开销,这些都再正常不过。但一上来就要求“全部给我”,那恐怕就不是信任,而是控制欲在作祟了。真打算好好过日子的人,通常会先一起商量规则,而不是着急先收走所有的钥匙。

面试题:原子的数量

乍一看这题名,很多人可能会往化学式上联想,结果越想越复杂。其实它跟化学知识没多大关系,核心考察的就是一件事:把一个带括号的字符串,按照作用域进行计数合并

比如 K4(ON(SO3)2)2,真正的难点根本不在开头的 K4,而在后半段那两层嵌套的括号和它们附带的倍数。这种时候如果还想着从左到右硬扫一遍,代码写着写着就容易乱:当前元素该算到哪一层?括号结束后那个倍率该乘给谁?前面已经统计过的数量怎么回填?

碰到这种题,我第一反应就不太推荐用递归硬怼,尤其是在字符串扫描过程中还要处理多位数字的情况。用的思路会更稳妥,脑子也更清楚:每进入一层括号,就开启一个新的计数表;每退出一层,就把当前层的计数乘上倍率,再合并回上一层。

整个思路可以拆解为四个清晰的步骤:

  1. 准备一个栈,里面存放 Map<String, Integer> 类型的计数表。栈底先压入一个总表。
  2. 扫描字符串时,遇到 (,就压入一个新的空表,表示进入新作用域。
  3. 遇到 ),就把栈顶这一层的表弹出,读取后面紧跟的倍数(如果没有数字则默认为1),将表中所有计数乘以该倍数后,合并到新的栈顶表(即上一层)中。
  4. 遇到大写字母(表示元素开头),则开始解析完整的元素名,再读取其后的数字(默认为1),并将这个计数累加到当前栈顶的表中。

代码不必追求花哨,能把细节处理干净才是关键:

import java.util.*;

public class Solution {
    public String countOfAtoms(String formula) {
        Deque<Map<String, Integer>> stack = new ArrayDeque<>();
        stack.push(new HashMap<>());

        int i = 0, n = formula.length();
        while (i < n) {
            char ch = formula.charAt(i);

            if (ch == '(') {
                stack.push(new HashMap<>());
                i++;
            } else if (ch == ')') {
                i++;
                int start = i;
                while (i < n && Character.isDigit(formula.charAt(i))) i++;
                int times = start < i ? Integer.parseInt(formula.substring(start, i)) : 1;

                Map<String, Integer> top = stack.pop();
                Map<String, Integer> cur = stack.peek();
                for (Map.Entry<String, Integer> e : top.entrySet()) {
                    cur.put(e.getKey(), cur.getOrDefault(e.getKey(), 0) + e.getValue() * times);
                }
            } else {
                int start = i++;
                while (i < n && Character.isLowerCase(formula.charAt(i))) i++;
                String atom = formula.substring(start, i);

                start = i;
                while (i < n && Character.isDigit(formula.charAt(i))) i++;
                int count = start < i ? Integer.parseInt(formula.substring(start, i)) : 1;

                Map<String, Integer> cur = stack.peek();
                cur.put(atom, cur.getOrDefault(atom, 0) + count);
            }
        }

        Map<String, Integer> ans = new TreeMap<>(stack.pop());
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Integer> e : ans.entrySet()) {
            sb.append(e.getKey());
            if (e.getValue() > 1) sb.append(e.getValue());
        }
        return sb.toString();
    }
}

这里有两个细节特别容易写错。一个是元素名可能不止一个字符,像 Mg,首字母大写,后面可能跟着若干小写字母。如果按单个字符处理,结果肯定不对。另一个是数字可能不存在,没有显式数字时默认计数是 1,这个分支如果漏了,测试用例一多就会出错。

最后输出为什么用 TreeMap?因为题目明确要求按字典序拼接元素。别前面辛苦算对了,最后在输出顺序上翻车,这种亏吃得有点冤。

这题本质上不是在模拟化学式,而是在模拟 “括号作用域内的计数聚合” 。你可以把每一层括号想象成 SQL 查询中的一个临时聚合表,出栈时统一乘以倍率再向上合并,整个过程就清晰多了。

题目的算法思路本身不复杂,麻烦在于细节多。字符串处理题经常这样,核心思路可能两分钟就想通了,但处理各种边界条件却要调试半小时。真正能拉开差距的,往往不是谁先想到用,而是谁的代码写完第一遍就能通过各种边界测试。

无论是处理生活里的财务边界,还是解决LeetCode上的面试难题,清晰的规则和稳健的“数据结构”都是关键。如果你对这类问题感兴趣,或者想找更多Java实战题解,不妨来云栈社区逛逛,这里有很多开发者在分享和讨论。




上一篇:AMD Vivado 2024.1 CPM5 QDMA Gen4x8 ST性能设计配置与测试实战
下一篇:Skills Desktop:Claude Code Skills 管理难题的实用工具分享
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-20 14:51 , Processed in 0.647163 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表