大厂P8,跳槽去小厂当副总,3个月被开了!
最近圈里有个真事:某位大厂P8,技术出身,带过三十来号人,手里也跑过那种“随便一写就是亿级PV”的项目。后来被一家拿了B轮的创业公司挖走,头衔直接拉满——副总兼CTO,工资涨两成,期权还给得像发年终红包。
入职那天更夸张,老板亲自全员邮件,话里话外就一个意思:以后技术听他的。我看完只想说,这邮件跟“我负责貌美如花”差不多,听听就好。
结果呢?三个月,人就“优化”了。你以为是能力不行?我觉得很多时候是剧本不对:大厂讲流程,小厂讲情绪;大厂看数据,小厂看老板当天心情。
头衔越大,背锅越快,尤其当你真的开始“按专业来”。
算法题:奇怪的打印机
那天晚上加班到快十二点,准备关电脑溜了,一个小伙子突然在工位后面冒出来,说东哥东哥,有个“奇怪的打印机”你帮我看下。 我当时脑子一抽,以为是工位旁边那台打印机又卡纸了,结果一听,是道算法题,瞬间精神了半格。
大概意思你应该知道,就是有个打印机挺轴的,它一次只能选一段连续区间,把这一段全打成同一个字符,而且这一段可以覆盖之前已经打印过的内容,相当于“涂改液模式”。问你最少要打几次,才能把一串字符串打出来。听起来有点抽象对吧,其实特别像我们写 PPT,反复选中一大段,全换成红色,再选中中间一段换成蓝色,那种操作。
我第一眼当然也想暴力嘛,左边打一段,右边打一段,中间再瞎折腾一下。直觉告诉我,这玩意儿肯定是区间 DP,毕竟那种“从 i 到 j 最优”的味道一闻就有。只是当时脑子有点糊,差点把状态转移写成屎山。
你先想象一下,我们搞个 dp[i][j],表示把子串 s[i…j] 打出来,最少要几次。最蠢的想法是:我先把 s[i] 单独打一遍,然后剩下 i+1…j 再搞,所以有一个很直接的式子:
dp[i][j] = dp[i + 1][j] + 1
意思就是:第一个字符自己先打一趟,再看后面怎么打。 但这样肯定不是最优的,比如字符串是 "aba",如果你先打 'a',再打 "ba",你可能会多干活。
神奇的点在于:如果后面某个位置 k 上,s[k] 跟 s[i] 是一样的,那这两个位置其实可以“顺便一起打”。就像你打印文档时,发现前后两个地方都要变成红色,你不会分两次选区,对吧,你会一次选大点的区间顺手搞定。
所以当 s[i] == s[k] 的时候,你可以考虑这样分:
i+1…k-1 这段先打好 然后在 k…j 这段打的时候,顺便把 i 这个位置也覆盖成同样的字符
于是有了另一个状态转移: dp[i][j] = min(dp[i][j], dp[i+1][k-1] + dp[k][j])
听起来有点绕?你可以粗暴地理解成: “我把 i 这一格的贡献挪到了 k 那一趟里,少了一次单独打印”。
我当时就拿白板乱画,嘴里还念叨着: “反正就是这个…怎么说…i 跟后面的同色一起打就行了…你先相信我……”
还有个小细节,语音识别一下就可能识别错: 像 "aaabbb" 这种连续相同字符,打印机一次就能搞定,你如果在状态里还当成三个 'a' 分开处理,纯属浪费算力,所以一般会先把字符串压缩一下,把连续相同的字符缩成一个。这个优化不做,题也能过,就是会慢一点。
我给那同学写了个 Python 版本,大概长这样:
defstrange_printer(s: str) -> int:
ifnot s:
return0
# 压缩一下连续相同字符,比如 "aaab" -> "ab"
t = []
for ch in s:
ifnot t or t[-1] != ch:
t.append(ch)
s = ''.join(t)
n = len(s)
dp = [[0] * n for _ in range(n)]
# 区间 DP,i 从右往左枚举
for i in range(n - 1, -1, -1):
dp[i][i] = 1# 单个字符,一次就够
for j in range(i + 1, n):
# 先假设把 s[i] 单独打一遍
dp[i][j] = dp[i + 1][j] + 1
# 再尝试和后面相同字符合并打印
for k in range(i + 1, j + 1):
if s[k] == s[i]:
# i 这一位挪到 k 那一轮一起打
cost = dp[i + 1][k - 1] + dp[k][j]
if cost < dp[i][j]:
dp[i][j] = cost
return dp[0][n - 1]
写完我习惯性跑个简单例子,"aba",脑袋里过一遍过程:
i=j 时,dp[0][0] = 1, dp[1][1] = 1, dp[2][2] = 1 对区间 [0,1],"ab":要么先打 'a' 再打 'b' → 2 [1,2],"ba" 也是 2 最后 [0,2],"aba": 先按蠢办法:dp[1][2] + 1 = 3 但发现 s[0] == s[2],可以让 0 和 2 一起打: 中间 [1,1] = 1,然后 dp[2][2] = 1,加起来 1 + 1 = 2 所以 dp[0][2] = 2,挺合理,一次打成 "aaa",一次覆盖中间变 "b"。
当时那小伙子在旁边一直嗯嗯嗯,结果等我写完他来一句:“东哥,这是不是那个区间 DP 经典题啊,我刷题的时候跳过去了…” 我:你这不是跳过去,是跳到我头上来了。
这个题其实挺有代表性的: 看上去是打印机,实质是“区间 + 覆盖 + 选择顺序影响成本”。以后你再碰到那种“子串 [i…j] 最小/最大什么”的题,脑子里就可以先冒出一个 dp[i][j] 试试看,八成跑不了。
行了我先去泡个咖啡,等会儿估计又有人来问我为啥时间复杂度是 n 的三次方…