女朋友要求我把所有的钱都给她管,合理吗?
“把工资卡交出来,我帮你管钱。”这话一出来,很多男的都表示,不合理呀
这事合理不合理,真别一句话下结论。谈恋爱不是公司报账,钱也不是爱情质检报告。你愿意上交,那叫信任加分;对方张口就要“全部归我管”,味道就有点不对了,像还没结婚先把财务自由给你办没了。
我看这种事,核心根本不是钱,是边界。正常过日子可以一起记账、商量存钱、定共同开销,谁都别装大掌柜。真要一分钱不留全交出去,回头连给自己买双袜子都得报备,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 里,先把“数字怎么取、原子怎么取、括号怎么改倍数”三件事捋顺,代码就不会丑。这个题不是难在算法多高级,是难在你能不能把细节管住。