程序员老鬼

女朋友要求我把所有的钱都给她管,合理吗?

“把工资卡交出来,我帮你管钱。”这话一出来,很多男的都表示,不合理呀

Image

这事合理不合理,真别一句话下结论。谈恋爱不是公司报账,钱也不是爱情质检报告。你愿意上交,那叫信任加分;对方张口就要“全部归我管”,味道就有点不对了,像还没结婚先把财务自由给你办没了。

我看这种事,核心根本不是钱,是边界。正常过日子可以一起记账、商量存钱、定共同开销,谁都别装大掌柜。真要一分钱不留全交出去,回头连给自己买双袜子都得报备,HR看了都得觉得这管理方式太硬了。

面试题:原子的数量

一看到 K4(ON(SO3)2)2 这种式子,很多人第一反应就是硬模拟,指针往后扫,遇到 ( 入栈,遇到 ) 出栈。思路没错,但代码十有八九写着写着就乱了,尤其是这种细节:元素名有大小写、数字可能是多位、括号后面还要乘倍数,最后还得按字典序输出。这个题麻烦的地方,不在“会不会”,在于你很容易漏一口气。

我一般不从左往右推。这个题从右往左更顺手。

原因很直接:数字是修饰前面的。你从右往左扫,先拿到倍数,再决定这个倍数该乘给谁,逻辑反而更稳。括号也一样,碰到 ) 说明后面这一段要整体乘一个系数,压栈;碰到 ( 再把这层系数弹掉。

核心就两个东西: 一个 Map<String, Integer> 统计原子数量; 一个栈维护“当前总倍数”。

代码我自己平时会这么写,尽量把解析逻辑拆小一点,不然主循环很快就看不下去了:

import java.util.*;

classSolution{
public String countOfAtoms(String formula){
        Map<String, Integer> cnt = new TreeMap<>();
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(1);

int n = formula.length();
int i = n - 1;
int num = 0;
int base = 1;
int multi = 1;

while (i >= 0) {
char c = formula.charAt(i);

if (Character.isDigit(c)) {
                num += (c - '0') * base;
                base *= 10;
                i--;
            } elseif (c == ')') {
int val = num == 0 ? 1 : num;
                multi *= val;
                stack.push(val);
                num = 0;
                base = 1;
                i--;
            } elseif (c == '(') {
                multi /= stack.pop();
                num = 0;
                base = 1;
                i--;
            } else {
int end = i;
while (i - 1 >= 0 && Character.isLowerCase(formula.charAt(i))) {
                    i--;
                }
if (i - 1 >= 0 && Character.isUpperCase(formula.charAt(i - 1))) {
                    i--;
                }
                String atom = formula.substring(i, end + 1);
int val = num == 0 ? 1 : num;
                cnt.put(atom, cnt.getOrDefault(atom, 0) + val * multi);

                num = 0;
                base = 1;
                i--;
            }
        }

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

这里面有两个地方最容易写歪。

第一个,元素名不是一个字符。像 Mg、Be,不能看见字母就直接记。要把大写字母和后面的若干小写字母一起吃掉。

第二个,倍数不是当前这一层的倍数,而是“外层所有括号乘起来的总倍数”。所以我上面直接用 multi 维护累计值,进括号乘进去,出括号除出来,省得你每次现算。

这题时间复杂度就是 O(n log k),n 是字符串长度,k 是原子种类数,主要耗在最后 TreeMap 的有序输出上。要是你先用 HashMap 统计,最后再排序,也一样能过。

这种题看着像字符串,实际上还是规规矩矩的解析题。别急着一把梭把所有分支揉进一个 while 里,先把“数字怎么取、原子怎么取、括号怎么改倍数”三件事捋顺,代码就不会丑。这个题不是难在算法多高级,是难在你能不能把细节管住。