某字节员工爆料:我们运营负责人,44岁,手里管:着几十个项目。他定了个死规矩:每周四晚上8点必须离开
刚刷到这个有点绷不住。说某字节运营负责人,44岁,一个人管几十个项目,结果定了个规矩:每周四晚上8点必须离开公司
明明是加班常态的地方,突然搞个“固定下班时间”,有点反直觉。你说是反内卷,还是单纯把节奏当管理手段,也不好说。
下面的人估计挺分裂,一边项目还在爆,一边领导先走了。打工人看了大概也就两种反应:要么羡慕,要么开始琢磨周四是不是才是最要命的一天。
一看到“把字符串里的数字替换成 number”,我第一反应不是上正则。
这种题看着小,最容易写出一段很舒服但不太稳的代码:
s = s.replaceAll("[0-9]", "number");
能跑,但我一般不这么写。算法题里没必要把正则引擎拉进来,而且这种写法把问题藏掉了:它到底扫了几遍?每次匹配怎么处理?字符串中间扩容多少次?这些东西面试官要是追一句,就容易虚。
这题的输入一般长这样:
a1b2c3
输出:
anumberbnumbercnumber
规则很简单:遇到字母,原样放进去;遇到数字,放入字符串 number。
所以我会直接按字符扫一遍。
不要在原字符串上改。Java 的 String 是不可变对象,你每拼一次,其实都是新对象。小数据看不出来,大一点就开始浪费。
关键代码就这么几行:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
publicclassMain{
publicstaticvoidmain(String[] args)throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String raw = reader.readLine();
StringBuilder fixed = new StringBuilder(raw.length() * 2);
for (int i = 0; i < raw.length(); i++) {
char ch = raw.charAt(i);
if (ch >= '0' && ch <= '9') {
fixed.append("number");
} else {
fixed.append(ch);
}
}
System.out.println(fixed);
}
}
这里有个小地方我会刻意这么写:
ch >= '0' && ch <= '9'
而不是:
Character.isDigit(ch)
不是说 Character.isDigit 错,而是这道题通常限定的是英文小写字母和数字,数字就是 ASCII 里的 0 到 9。用范围判断更直接,不会把 Unicode 里一些奇怪的数字字符也算进去。
StringBuilder 的初始容量这里写了:
raw.length() * 2
这不是精确值,只是少扩几次容。最精确的做法是先扫一遍统计数字个数,再算最终长度:
最终长度 = 原长度 + 数字个数 * 5
因为一个数字原来占 1 个字符,替换成 number 以后占 6 个字符,多出来 5 个。
但这题没必要搞两遍。除非输入特别大,或者你明确被要求原地扩容处理,否则一遍扫描已经够干净了。
时间复杂度就是 O(n),每个字符只看一次。
空间复杂度也是 O(n),因为结果字符串肯定要重新存一份。别在这里硬说 O(1),输出本身就变长了。
这题真正想考的不是“会不会替换”,而是你写代码时会不会下意识避开低效字符串拼接。
比如下面这种,我看到一般会皱一下眉:
String ans = "";
for (int i = 0; i < s.length(); i++) {
ans += s.charAt(i);
}
这不是不能跑,是跑得没必要。每次 += 都在造新字符串,循环一长,成本就堆上来了。
所以这类字符串处理题,记住一个顺手的排查顺序:
先看是不是逐字符处理。
再看是不是需要扩容。
最后再决定用 StringBuilder,别上来就正则一把梭。
小题写顺手了,大字符串清洗、日志字段替换、接口参数脱敏,都是这套东西。