刚看到个帖子,说有面试官面了个要28K的候选人,简历写“精通Linux/Shell”。让他现场写个命令统计Nginx日志里访问量Top10 IP,人家憋半天只写出个cat,后面就彻底卡死了。

怎么说呢,我觉得这事关键不在紧不紧张,而是要价和实力对不对得上。28K不是小数,指望公司按“精通价”付钱,那至少得有一套拿得出手的命令组合吧。
简历上“精通”两个字,很多人当“格式词”,但在HR眼里就是白纸黑字的承诺。你写“熟悉”还情有可原,写“精通”却连awk、sort、uniq都用不出来,这就有点像点了份豪华套餐,结果端上来一盘拍黄瓜。
我的看法是:别怕写自己“一般”“入门”,但真别乱写“精通”。职场说白了就是:啥价配啥货,踏实把本事练扎实,比堆漂亮形容词靠谱多了。
昨晚十一点多,我在公司楼下等外卖,群里一个实习生突然丢过来一句: “东哥,那个整数转罗马数字你咋写啊,我每次都写成一坨 if else …”
我当时哈欠都打出来了,只能给他语音讲了一遍,顺手把代码丢给他。今天就把这个过程整理一下,你下次再遇到这种题,脑子里能直接浮现思路,不用瞎蒙。
题目先说清楚是啥
大概意思就是:给你一个 1 到 3999 的整数,比如 1994,要你返回对应的罗马数字字符串,比如 "MCMXCIV"。
罗马数字大概就那几个符号: I=1,V=5,X=10,L=50,C=100,D=500,M=1000,外加一些“减法组合”: 4=IV,9=IX,40=XL,90=XC,400=CD,900=CM。
关键点就俩: 1)要支持这些减法组合 2)要按“从大到小”拼起来
别一上来就 if-else 炸裂,容易把自己绕晕。
先想想人是怎么写罗马数字的
你看 1994 这个数,人类一般会这么“拆”: 1000 -> M 900 -> CM 90 -> XC 4 -> IV
然后一拼:M + CM + XC + IV = MCMXCIV。
这不就是“每次尽量用最大的罗马数字往上凑”,直到数字被减到 0 吗? 这就是所谓的“贪心写法”,听起来高大上,其实就一句话:能用大的就不用小的,能减就减到不能减为止。
把这个思路翻译成代码
用 Python 写,其实就两行配置 + 一个循环。
defint_to_roman(num: int) -> str:# 从大到小把所有可能用到的数值和符号列出来 values = [1000, 900, 500, 400,100, 90, 50, 40,10, 9, 5, 4,1] symbols = ["M", "CM", "D", "CD","C", "XC", "L", "XL","X", "IX", "V", "IV","I"] res = []for v, s in zip(values, symbols):if num == 0:break# 看现在这个数里能包含多少个 v count, num = divmod(num, v)if count: res.append(s * count)return"".join(res)你可以自己随便测几个:
print(int_to_roman(3)) # IIIprint(int_to_roman(4)) # IVprint(int_to_roman(58)) # LVIIIprint(int_to_roman(1994)) # MCMXCIV为啥这个写法靠谱
你可以这么理解一下: 罗马数字的规则本身就是“从大到小排列”,中间只能偶尔插一个减法对儿(比如 900、400 这种),而我们 values 这个数组,已经把所有合法的“块”都列出来了,而且是从大到小排好的。
那你每次都优先用能放进去的最大那一个,最后得到的一定是:
有点像你在超市找零,你手上有 100、50、20、10、5、1 这些纸币和硬币,每次都是先用面值最大的那个去凑,最后张数也比较少,对吧。
顺便说一下复杂度
这题经常有人问时间复杂度,其实没啥好算的。values 这个数组长度是 13,循环最多也就跑 13 次,跟 num 多大关系都不大,所以可以当成 O(1)。 从“刷题面试”的角度,你说 O(1) 或者“常数复杂度”就行了,面试官不会跟你抠这个。
几种常见的“错误姿势”
那个实习生一开始是这么写的:
逻辑没错,就是非常啰嗦,而且每一位的代码都差不多,一改就得改四份,特别不优雅。
贪心的写法,相当于把“每一位”的情况统一成一套规则,整段逻辑一步走完,看着舒服很多,也方便记。
如果你想再抽象一点
上面那份代码已经够用了,但你要是写库、或者想优点“强迫症封装”,可以把配置单独拎出来:
classRomanConverter:def__init__(self): self.values = [1000, 900, 500, 400,100, 90, 50, 40,10, 9, 5, 4,1] self.symbols = ["M", "CM", "D", "CD","C", "XC", "L", "XL","X", "IX", "V", "IV","I"]defto_roman(self, num: int) -> str: res = []for v, s in zip(self.values, self.symbols):if num == 0:break count, num = divmod(num, v)if count: res.append(s * count)return"".join(res)之后就可以:
conv = RomanConverter()print(conv.to_roman(2024))这样你哪天要扩展,比如再写个 from_roman 做反向转换,也有个地方挂。
这种经典题其实就两个事: 一个是把人脑里的“自然做法”说清楚; 另一个是想办法把规则压缩成一套稳定的模板(这里就是那两个数组 + 贪心循环)。
多写几次,你再在面试里遇到,心里就不会慌了。 行了,我先去冲杯咖啡,你可以把这段代码敲一遍跑跑,看有没有哪里没太懂的地方。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB