面试了个36岁的大哥,技术底子那是真硬,架构设计头头是道,甚至还能手写源码,才18k。结果HR那边直接卡住...
刚看到个贴子,说是面了个36岁的大哥,技术稳得一批,架构设计说得明明白白,薪资也就十八K,结果被 HR 以“没潜力、性价比低”给卡了。
网友们也有说“年轻便宜好用”,但我觉得有点片面。说到底,公司招人是为了干活,不是为了做公益。一个能直接上手的人,被嫌弃潜力不够,不懂怎么想的!
换个角度想,30+ 的价值不是潜力,而是稳定、经验、靠谱,这些在项目里才是真金白银。有网友说年轻人便宜,但便宜往往要老员工擦屁股,这算哪门子性价比?
不过话说回来,职场嘛,双方都在算账。希望企业别把“潜力”当挡箭牌,也别忽略成熟人才的价值。【备注:文末可领最新资料】
面试题:变更性别
写这个题之前,咱先把“变更性别”这仨字翻成程序员听得懂的话:一串男女同学排队,老师老喜欢搞事情——“把 2 到 5 号同学性别都反一下”“问问 3 到 7 号里面有几个男生”……你要写个程序,把这些操作都快速处理了。
我们自己把题意定完整一点(方便讲算法):
有
n个同学,从 1 到 n 排队。用一个长度为 n 的字符串表示初始性别:
'M'表示男(male)'F'表示女(female)之后有
q次操作,两种类型:
C l r:把区间[l, r]里的性别全部翻转
男变女、女变男
Q l r:询问区间 [l, r] 里有多少个男生
要求:n 和 q 都可能是 10^5 级别的,如果你每次都一个一个去改、一个一个去数,肯定超时。
暴力做法为啥不行?
最朴素的想法:
用一个列表存性别,比如
['M', 'F', 'M', ...]遇到
C l r:for i in [l, r] 把 'M'换成'F',反之亦然,复杂度 O(r-l+1)遇到
Q l r:for i in [l, r] 数一数 'M'的个数,也是 O(r-l+1)
最坏情况:每个操作都改 / 查整个数组,就是 q * n10^5 * 10^5 = 10^10,这在 Python 里铁定超时。
所以核心问题就一句话:怎么在支持区间翻转的同时,又能快速统计区间里有多少男生?
把性别数字化一下
先做一件小事:把性别转成 0/1,更好算:
定义 1表示男生0表示女生
那一个区间 [l, r] 里男生数量,就是这个区间的“和”。
翻转性别就变成:
0 变 1 1 变 0
如果区间长度是 len,原来这段区间里有 sum 个男生,翻转之后:
女生变男生: len - sum男生变女生: sum变女生数量 所以 **翻转后的男生数 =len - sum**。
也就是说: 对一个区间做“翻转”操作,其实就是把这个区间的“男生数量”改成 len - 原来的值。这点很重要,等会儿用在线段树的懒标记里。
为啥要上线段树 + 懒标记?
我们要支持两种区间操作:
区间翻转: C l r区间求和: Q l r(统计男生数)
非常标准的“区间修改 + 区间查询”场景,用 线段树(Segment Tree)+ 懒惰标记(Lazy Propagation) 就比较合理:
在线段树每个节点里存三样信息:
l, r:这个节点代表的区间左右端点sum:这个区间里有多少男生lazy_flip:一个布尔值(0/1),表示“这一段是否需要翻转但还没往下传”
关键逻辑:
当对一个节点
[l, r]做“整段翻转”时:
它的 sum变成len - sumlazy_flip^= 1(翻转标记异或一下,相当于“再来一刀又翻回来”)
当你要往下递归(访问子节点)之前,如果当前节点有 lazy_flip:
把这个懒标记推给左右孩子 同样更新左右孩子的 sum和lazy_flip然后把当前节点的 lazy_flip清零
这样,每次区间修改和区间查询的复杂度都是 O(log n),10^5 级别完全扛得住。
下面直接上一个可跑的实现,默认输入格式类似:
n q
MFMFMF...
C 2 5
Q 1 3
...
代码:
import sys
sys.setrecursionlimit(1_000_000)
classSegmentTree:
def__init__(self, arr):
self.n = len(arr)
# 开 4n 比较保险
self.sum = [0] * (4 * self.n) # 区间男生数量
self.lazy = [0] * (4 * self.n) # 懒标记:是否需要翻转
self._build(1, 1, self.n, arr)
def_build(self, idx, l, r, arr):
"""建树:把初始性别数组灌进线段树"""
if l == r:
self.sum[idx] = arr[l - 1] # arr 是 0-based,下标注意一下
return
mid = (l + r) // 2
self._build(idx * 2, l, mid, arr)
self._build(idx * 2 + 1, mid + 1, r, arr)
self._push_up(idx)
def_push_up(self, idx):
"""父节点的 sum = 左子 + 右子"""
self.sum[idx] = self.sum[idx * 2] + self.sum[idx * 2 + 1]
def_apply_flip(self, idx, l, r):
"""对当前节点整段翻转"""
length = r - l + 1
self.sum[idx] = length - self.sum[idx]
self.lazy[idx] ^= 1# 懒标记取反(0->1,1->0)
def_push_down(self, idx, l, r):
"""把当前节点的懒标记往下传"""
if self.lazy[idx] == 0:
return
mid = (l + r) // 2
# 左孩子
self._apply_flip(idx * 2, l, mid)
# 右孩子
self._apply_flip(idx * 2 + 1, mid + 1, r)
# 清除当前懒标记
self.lazy[idx] = 0
defupdate_range_flip(self, ql, qr, idx=1, l=1, r=None):
"""把区间 [ql, qr] 性别翻转"""
if r isNone:
r = self.n
# 不相交
if qr < l or ql > r:
return
# 完全覆盖
if ql <= l and r <= qr:
self._apply_flip(idx, l, r)
return
# 部分覆盖,往下递归
self._push_down(idx, l, r)
mid = (l + r) // 2
self.update_range_flip(ql, qr, idx * 2, l, mid)
self.update_range_flip(ql, qr, idx * 2 + 1, mid + 1, r)
self._push_up(idx)
defquery_range_sum(self, ql, qr, idx=1, l=1, r=None):
"""查询 [ql, qr] 里有多少男生"""
if r isNone:
r = self.n
# 不相交
if qr < l or ql > r:
return0
# 完全覆盖
if ql <= l and r <= qr:
return self.sum[idx]
# 部分覆盖,下推懒标记
self._push_down(idx, l, r)
mid = (l + r) // 2
left_sum = self.query_range_sum(ql, qr, idx * 2, l, mid)
right_sum = self.query_range_sum(ql, qr, idx * 2 + 1, mid + 1, r)
return left_sum + right_sum
defmain():
input = sys.stdin.readline
n, q = map(int, input().split())
s = input().strip() # 形如 "MFMFFM..."
# 把性别转成 0/1:男=1,女=0
arr = [1if ch == 'M'else0for ch in s]
seg = SegmentTree(arr)
output = []
for _ in range(q):
line = input().split()
op = line[0]
l = int(line[1])
r = int(line[2])
if op == 'C':
# 区间翻转
seg.update_range_flip(l, r)
elif op == 'Q':
# 查询区间男生数量
res = seg.query_range_sum(l, r)
output.append(str(res))
sys.stdout.write("\n".join(output))
if __name__ == "__main__":
main()
把男女变成 0/1,问题就变成“区间翻转 + 区间求和” 翻转这件事,本质是: sum -> len - sum用线段树维护“每一段里有多少个男生”,再用懒标记记住“这段以后要不要翻转” 这样每次操作都只是 log n级别,n, q上到 10^5 也能稳住
你如果想换玩法,比如把查询变成“问第 x 个同学现在是男是女”,也很好改: 用 Q x x 查一下这个位置的值,1 就男、0 就女,开个 if 输出 'M' 或 'F' 就行了。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB