连续两个月裁员,果然没逃过。心情还行,因为我旁边的博士也被裁了
刚刷到这个,给我看得有点想笑,又有点心酸。
有网友说,公司这两个月一直在裁人,一波接一波,本来还想着自己是不是能躲过去,结果还是没跑掉。更绝的是,他心态居然还挺稳,因为坐他旁边那个博士也一起被裁了。
这就很职场。你以为自己不够强才被优化,回头一看,学历高的、项目多的、平时看着很稳的,也照样被打包带走。那一瞬间人反而释怀了:哦,原来不是我菜,是公司真不做人。
打工人最怕的不是被裁,是被裁完还自我怀疑。结果旁边博士一走,直接给他完成心理疏导了。HR都不用安慰,工位旁边这位已经替公司把情绪价值给满上了。
这题一眼看过去,最容易写成两层循环。
s = "abc",shifts = [3, 5, 9]。
意思是:
第 0 次,把前 1 个字母右移 3 次;
第 1 次,把前 2 个字母右移 5 次;
第 2 次,把前 3 个字母右移 9 次。
最后结果是:
abc
dbc
igc
rpl
如果直接按题意模拟,代码大概会写成这样:
defshift_slow(s, shifts):
chars = list(s)
for end, step in enumerate(shifts):
move = step % 26
for i in range(end + 1):
old = ord(chars[i]) - ord('a')
chars[i] = chr((old + move) % 26 + ord('a'))
return''.join(chars)
这段代码没毛病,但我第一眼就不太信它能过大数据。
因为它每次都在反复改前缀。
shifts 长度如果是 10 万,外层 10 万,内层平均 5 万,这就不是算法题了,这是给 CPU 烧香。
这题真正要看的不是“怎么移动字母”,而是“每个位置到底被移动了多少次”。
拿 abc 来看:
a 会受到 shifts[0] + shifts[1] + shifts[2]
b 会受到 shifts[1] + shifts[2]
c 会受到 shifts[2]
也就是说,每个位置受到的是它右边那一段的累加值。
所以不用从左往右模拟操作,反过来从右往左扫一遍就行。
扫的时候维护一个 total_shift,表示当前位置最终要右移多少次。
代码我一般会这样写:
defshifting_letters(s, shifts):
if len(s) != len(shifts):
raise ValueError("s 和 shifts 长度不一致,这种数据别继续算")
chars = list(s)
total_shift = 0
base = ord('a')
for i in range(len(chars) - 1, -1, -1):
total_shift = (total_shift + shifts[i]) % 26
old_pos = ord(chars[i]) - base
new_pos = (old_pos + total_shift) % 26
chars[i] = chr(base + new_pos)
return''.join(chars)
跑一下刚才那个例子:
print(shifting_letters("abc", [3, 5, 9]))
输出:
rpl
这里有个小细节,total_shift 每次都要 % 26。
不是为了正确性才必须立刻取模,Python 大整数也能扛一阵。但没必要让它一直涨。字母一共就 26 个,右移 27 次和右移 1 次没区别。
再看一个边界:
print(shifting_letters("z", [1]))
输出:
a
这个地方如果忘了取模,或者字符位置算错,很容易把 z 移到 {,这类 bug 看着小,线上做字段脱敏、券码生成、简单编码时也真见过。
这题最后其实就一句话:别模拟每一次前缀移动,直接算每个字符最终被移动的总次数。
时间复杂度从 O(n²) 降到 O(n)。
空间上除了结果数组,没有额外大东西。
我不太建议这题上来就背“后缀和”三个字。先把每个字符被哪些操作影响写出来,规律自然就出来了。算法题有时候不是不会写,是一开始就顺着题意硬干,方向歪了。