通过率 0% · 提交 0 · 通过 0
推理服务维护 S 个 Prefix Cache 槽位,每个槽位保存一段 token 序列和当前负载。每来一个请求,需要选择一个槽位复用最长公共前缀;如果多个槽位的公共前缀长度相同,选择当前负载更小的槽位;仍并列则选择编号更小的槽位。请求处理后,该槽位的负载增加未命中的 token 数,并将缓存序列更新为本次请求序列。
这类题属于算法机考高频题型中「华为 AI 岗 / KV Cache」方向的高频题型,通常考察对「华为 AI 岗 / KV Cache」的建模能力与边界条件处理。掌握本题的解题思路后,可举一反三应对同类真题方向,稳步提升机考通过率。
第一行输入 S Q。接下来 S 行,每行输入 load len 和 len 个 token,表示一个缓存槽位。随后 Q 行,每行输入 len 和 len 个 token,表示一个请求。
输出 Q 行。每行包含选择的槽位编号和复用的前缀长度。槽位编号从 1 开始。
示例 1
输入示例
3 2 3 3 1 2 3 1 3 1 2 4 0 1 9 3 1 2 5 2 9 8
输出示例
2 2 3 1
选择最长公共前缀
示例 2
输入示例
2 1 5 3 4 5 6 2 2 4 5 3 4 5 6
输出示例
1 3
完全命中缓存
时间限制 3000 ms · 内存限制 256 MB
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。
这些是真正决定能不能 AC、但通用题解里常被略过的点。
LLM 推理服务的 Prefix Cache 槽位选择模拟。算法只有「逐槽位比公共前缀」,考的是三级并列规则和状态更新的先后顺序——典型的「规则密集型」机考题。
对当前请求,逐个槽位计算它的缓存序列与请求序列的最长公共前缀长度(从头逐 token 比到第一个不同处)。选槽位按三级优先:
1. 公共前缀更长者优先; 2. 前缀一样长,当前负载更小者优先; 3. 仍并列,编号更小者优先。
实现上用一个三元组作比较键:(-prefixLen, load, id) 取最小,一次遍历完成,不需要排序。
两个动作,顺序和数值都别想当然:
len(请求) − prefixLen,不是整个请求长度,也不是前缀长度。命中的部分复用缓存,只有没命中的 token 产生新计算量。输出的槽位编号从 1 开始,内部若用 0 基下标记得 +1。
best = None
for i, (load, seq) in enumerate(slots):
p = 0
while p < len(seq) and p < len(req) and seq[p] == req[p]:
p += 1
key = (-p, load, i)
if best is None or key < best[0]:
best = (key, i, p)
_, sid, p = best
slots[sid][0] += len(req) - p # 负载加未命中数
slots[sid][1] = req # 缓存整体替换时间 O(S·Q·L),L 为序列最长长度;空间 O(S·L)。规模内直接双层循环即可。
样例 1:三个槽位 (load=3, 缓存 1 2 3)、(load=1, 缓存 1 2 4)、(load=0, 缓存 9),两个请求。
请求 1 2 5:三个槽位的公共前缀分别是 2、2、0。前缀并列在槽 1 和槽 2 之间,比负载:3 对 1,选负载小的槽 2,输出 2 2。更新:负载 1 + (3−2) = 2,缓存替换为 1 2 5。
请求 9 8:槽 1、2 前缀 0,槽 3 前缀 1(9 命中后缓存耗尽),选槽 3,输出 3 1。第一问的更新如果忘了做,第二问面对的槽位状态就全是错的——状态题错一步废全局。
Prefix Cache 是 LLM 推理工程的真实机制,面试口径:多个请求共享相同的前缀(系统提示词、few-shot 模板)时,前缀部分的 KV 已经算过,命中缓存就只为未命中的后缀付出计算——这道题里「负载加未命中数」正是这笔账的抽象。追问「为什么并列时选负载小的槽位」:这是最朴素的负载均衡,避免热门前缀把单一槽位打爆。把一道模拟题讲成「我理解 vLLM 这类推理框架为什么做 prefix caching」,是这题在面试里的正确打开方式。
# 逐槽位算最长公共前缀,比较键=(-前缀长,负载,编号)取最小;
# 选中后负载加未命中数(请求长−前缀长),缓存序列整体替换为本次请求。
import sys
def lcp(a, b):
i = 0
limit = min(len(a), len(b))
while i < limit and a[i] == b[i]:
i += 1
return i
def solve() -> None:
data = list(map(int, sys.stdin.buffer.read().split()))
if not data:
return
at = 0
s = data[at]; at += 1
q = data[at]; at += 1
slots = []
for _ in range(s):
load = data[at]; at += 1
length = data[at]; at += 1
seq = data[at:at + length]; at += length
slots.append([load, seq])
ans = []
for _ in range(q):
length = data[at]; at += 1
req = data[at:at + length]; at += length
best = 0
best_prefix = -1
for i, (load, seq) in enumerate(slots):
prefix = lcp(seq, req)
if prefix > best_prefix or (prefix == best_prefix and (load < slots[best][0] or (load == slots[best][0] and i < best))):
best = i
best_prefix = prefix
ans.append(f"{best + 1} {best_prefix}")
slots[best][0] += len(req) - best_prefix
slots[best][1] = req
print("\n".join(ans))
if __name__ == "__main__":
solve()
登录后可查看你在本题的历史提交,以及每次的各用例通过情况。