爱奇艺2020校招算法岗笔试第二场,虽然过去有一阵子了,但这套卷子在我带过的几届学弟学妹中反复被提起,原因很简单:它不像某些厂笔试那样专抠冷门算法题,而是把数据结构、机器学习基础、深度学习考点和视频业务场景全都揉在了一起,覆盖面广、梯度分明,非常适合拿来当算法岗校招笔试的“体检表”。
这篇文章我就按“现场复盘”的方式来写,把第二场里出现频次高、丢分多的考点逐个掰开讲一遍。不论你是正在准备秋招、还是工作几年想回来补基础,照着这套思路过一遍,都会比盲目刷题更高效。我会把每道题的解题逻辑、容易踩的坑、以及同类题的复习方向都讲到,尽可能还原我当时做题的真实思路。
1. 试卷整体结构与考察逻辑
1.1 题型分布与时间安排
先说整体感受。这套题满分100分,考试时间大概90到120分钟,题型分三块:选择题、填空题/简答题、编程题。选择题占大头,覆盖机器学习、深度学习、数据结构、概率论和线性代数;编程题一般两道,一道偏经典算法,一道偏业务场景模拟。
我印象里比较清晰的题量安排如下:
| 题型 | 题量 | 建议用时 | 考察重点 |
|---|---|---|---|
| 单选题 | 15题左右 | 25分钟 | 机器学习基础、深度学习、概率统计 |
| 多选题 | 5题左右 | 10分钟 | 易混淆概念、工程经验 |
| 填空题/简答 | 3题左右 | 15分钟 | 算法原理推导、模型结构理解 |
| 编程题 | 2题 | 40分钟 | 数据结构、动态规划、贪心算法 |
考试时间看起来宽裕,实际上很紧张。尤其是编程题,很多人不是不会做,而是前面选择题纠结太久,导致后面代码没时间调完。我的建议是:选择题每道不超过1分半,拿不准的先标记,不要恋战,后面检查时再回头想。
1.2 考察侧重点与命题风格
爱奇艺的算法笔试第二场有个显著特点:不追求偏怪难,而是考察“基本概念是否真懂”和“代码能否一遍写对”。同样是考KMP,它不会直接让你背next数组,而是给一个具体模式串,让你算next数组;同样是考动态规划,它会用视频推荐、字幕匹配这种业务场景来包装,但内核还是经典模型。
从考点分布来看,机器学习占比最高,大概40%左右;数据结构与算法占30%;深度学习和业务场景题占30%。这和爱奇艺以视频为核心、算法大量用于推荐、搜索、审核的业务形态有很大关系。
另外提醒一下,很多题表面是选择题,实际上要求你动笔算。比如给你一个样本集,让算信息增益;给你一个SVM的间隔表达式,让判断哪个点是支持向量。这类题没法靠“感觉”蒙,必须对公式和计算过程熟。
2. 数据结构与算法真题解析
2.1 字符串与KMP算法:next数组的计算
第二场笔试题里有一道很经典的KMP题,原题大意是:对于模式串p="abacaba",求其next数组。这里有个大坑:不同教材对next数组的定义不完全一样,有的是“最长相同前后缀长度”,有的是“失配时模式串跳转的位置”。如果你不先确认题目定义,直接套自己背的模板,很容易整道题全错。
我按最常见的一种定义来算:next[i]表示当模式串第i个字符(从0开始)失配时,模式串指针应该回退到的位置,其中next[0] = -1,而next[i] = 前缀函数prefix[i-1]。
先求p="abacaba"的prefix数组(prefix[i]表示p[0..i]的最长相等真前后缀长度):
p[0] = 'a',prefix[0] = 0p[0..1] = "ab",没有相等前后缀,prefix[1] = 0p[0..2] = "aba",最长相等前后缀是"a",prefix[2] = 1p[0..3] = "abac",没有,prefix[3] = 0p[0..4] = "abaca",最长是"a",prefix[4] = 1p[0..5] = "abacab",最长是"ab",prefix[5] = 2p[0..6] = "abacaba",最长是"aba",prefix[6] = 3
所以prefix数组为[0, 0, 1, 0, 1, 2, 3]。按next[0]=-1, next[i]=prefix[i-1]换算,得到:
next = [-1, 0, 0, 1, 0, 1, 2]这里要注意:如果题目使用另一种定义,直接把prefix数组当作next数组,结果就成了[0, 0, 1, 0, 1, 2, 3]。两种版本在牛客、力扣、王道等资料里都能见到,所以考试时先花10秒钟看题目给的是哪种定义,比闷头算更稳。
KMP的代码实现也要能默写,尤其是求next数组的递推过程。我用Java写了一个版本,思路是“双指针+回溯”:
public int[] getNext(String p) { int n = p.length(); int[] next = new int[n]; next[0] = -1; int i = 0, j = -1; while (i < n - 1) { if (j == -1 || p.charAt(i) == p.charAt(j)) { i++; j++; next[i] = j; } else { j = next[j]; } } return next; }这段代码里最容易被忽视的是else分支的回退逻辑:当字符不匹配时,j要跳转到next[j],而不是简单的j--。很多人在笔试时就是因为这里写错,导致KMP退化成O(n*m)。
2.2 排序与分治思想的变体考察
排序算法在校招笔试里从不会缺席,但爱奇艺第二场并没有直接让你手写快排,而是考察排序思想的变体。有一道印象很深的题:给一个未排序数组,要求找出第K大的数,时间复杂度越优越好。
本质上这就是快速选择算法(Quick Select),核心思路借鉴快速排序的partition操作:每次选一个基准元素,把数组分成小于基准和大于基准两部分,然后判断第K大落在哪一侧,只递归那一侧。
Python实现如下:
import random def findKthLargest(nums, k): def partition(left, right): pivot_idx = random.randint(left, right) pivot = nums[pivot_idx] nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] store = left for i in range(left, right): if nums[i] > pivot: nums[i], nums[store] = nums[store], nums[i] store += 1 nums[right], nums[store] = nums[store], nums[right] return store left, right = 0, len(nums) - 1 while True: pos = partition(left, right) if pos == k - 1: return nums[pos] elif pos < k - 1: left = pos + 1 else: right = pos - 1时间复杂度平均O(n),最坏O(n^2)。通过随机选择基准元素来避免最坏情况,是面试中需要主动说出来的优化点。如果你只回答“先排序再取第K个”,虽然答案对,但复杂度O(n log n)不是最优,在笔试中只能拿一半分。
类似的变体还有:求数组前K个高频元素、求中位数、求最小的K个数。复习时可以把它们归为一类——凡是和“第K个/前K个”有关的问题,优先想堆排序和快速选择,前者适合海量数据、后者适合数组可改的场景。
2.3 动态规划与贪心策略的选择题剖析
编程题里有一道典型的区间调度变体,背景改成了“视频上传任务调度”:给定N个任务的开始时间和结束时间,每个任务完成后可以释放审核资源,问一天内最多能完成多少个任务。这个场景本质就是经典贪心问题——按结束时间排序,每次选择结束最早且不与当前时间冲突的任务。
证明思路也要掌握:如果存在一个最优解,它的第一个任务不是结束时间最早的那个,那么用结束时间最早的任务替换它,不会与其他任务冲突,所以贪心选择安全。这类“贪心选择性+最优子结构”的证明套路,在简答题里经常考。
另一道编程题是动态规划的包装题,题干大概是“计算两个视频标题文本的相似度”,实际要求是求最长公共子序列长度。状态转移方程如下:
dp[i][j] = dp[i-1][j-1] + 1, 当 s1[i-1] == s2[j-1] dp[i][j] = max(dp[i-1][j], dp[i][j-1]), 当不相等注意边界条件是dp[0][j]和dp[i][0]都等于0,因为空字符串和任意字符串的公共子序列长度为0。如果题目要求输出具体子序列,还需要额外开一个direction数组记录每个状态是从哪里转移来的,考试时建议先写长度版本,如果时间充裕再扩展回溯逻辑。
3. 机器学习与深度学习基础题
3.1 基础概念题:这些分必须拿稳
爱奇艺第二场笔试题给人的感觉是,机器学习基础概念考得非常细,几乎没有送分题。我整理了几道典型题目和对应的复习要点,你们可以直接对照自查。
有一道多选题问“下列哪些方法可以缓解过拟合”,选项包括:L1正则化、L2正则化、Dropout、数据增强、增大模型参数量。正确答案是前四个。很多人会把L1和L2搞混,其实两者都能抑制过拟合,L1还会带来稀疏性,适合做特征选择。增大模型参数量会加重过拟合,属于反向操作。这类题没有技巧,只能靠平时积累。
另一道题关于Batch Normalization,问“以下关于BN的说法错误的是”。BN的核心作用是缓解内部协变量偏移,让每一层输入分布稳定,从而可以使用更大的学习率、加快收敛。但要注意,BN在训练时使用当前batch的均值和方差,在推理时使用训练阶段累积的全局统计量,很多人在这里丢分是因为混淆了训练和推理两个阶段的行为。
还有一道题考察SVM中核函数的作用:把低维空间线性不可分的数据映射到高维空间,使其线性可分。许多人的误区是“核函数降低了计算复杂度”,严格来说,核技巧避免的是“在高维空间中显式计算内积”,而不是“降低映射本身的复杂度”。这个表述差异在选择题里就是送命题。
3.2 模型训练中的常见问题
这一节我认为是整套卷子里最“拉分”的部分,因为单纯背书的人在这里会暴露。比如考过一个问题:“训练深度神经网络时,梯度消失的根本原因是什么?”答案是链式法则连乘导致梯度逐层衰减,尤其是在使用Sigmoid激活函数时,导数最大只有0.25,多层累乘后梯度迅速趋近于0。
引申出来的考点是激活函数的选择。ReLU之所以被广泛使用,是因为它在正半轴的导数为1,可以缓解梯度消失;但ReLU也有“神经元死亡”问题,即输入为负时梯度恒为0,一旦某个神经元落入这个区间,就很难再恢复。Leaky ReLU和ELU就是针对这个问题提出的改进,笔试中常考它们之间的差异。
关于集成学习,爱奇艺第二场考过Bagging和Boosting的对比。我用一张表来总结,方便你们记忆:
| 维度 | Bagging | Boosting |
|---|---|---|
| 样本采样 | 有放回采样,各模型独立 | 每轮调整样本权重 |
| 模型训练 | 可并行 | 串行 |
| 目标 | 降低方差 | 降低偏差 |
| 代表算法 | 随机森林 | GBDT、XGBoost、AdaBoost |
| 对异常值敏感度 | 相对不敏感 | 较敏感 |
我当年做这类题的经验是:不要死记“谁降低方差谁降低偏差”,而是从机制上理解。Bagging通过多个模型投票/平均来平滑掉个别模型的波动,所以降方差;Boosting每一轮都在拟合前一轮的残差,逐步逼近真实值,所以降偏差。
这里补充一个容易被考到的细节:随机森林的随机性来自两个维度,一个是样本的随机采样,一个是特征的随机子集。如果题目问“随机森林中每棵树训练时使用了多少样本”,答案是大约63.2%的原始样本,因为有放回采样中约有36.8%的样本从未被抽中,这些样本叫袋外数据(OOB),可以直接用来做无偏验证。这个考点在选择题里出现率很高,很多人不知道。
3.3 深度学习经典考点:感受野与Attention机制
深度学习部分,爱奇艺第二场考得比较克制,但每一题都很有代表性。有一道题问:堆叠3个3×3卷积(步长为1,padding为1),其感受野相当于一个多大的卷积?答案是7×7。推导过程是:第一个3×3卷积后,每个输出像素对应输入3×3区域;第二个3×3卷积后,对应输入5×5区域;第三个后对应7×7区域。这也是为什么VGG等网络喜欢用多个小卷积核替代大卷积核,参数更少、感受野相同、非线性表达能力更强。
Attention机制相关的题也出现过,比如“为什么Transformer中的Self-Attention能缓解长距离依赖问题”。传统RNN处理长序列时,信息需要经过多个时间步逐步传递,容易丢失或衰减;而Self-Attention直接计算序列中任意两个位置之间的依赖权重,一步到位,所以能更好地捕捉长距离关系。关键要理解Query、Key、Value三个向量的作用:Query关注目标位置要“找什么”,Key是当前位置“能提供什么”,Value是“实际提供的内容”,注意力权重就是Query和Key的相似度经过Softmax后的结果。
Word2Vec也考过,主要区分CBOW和Skip-gram。CBOW用上下文预测中心词,适合小数据集;Skip-gram用中心词预测上下文,对低频词效果更好。这个点是自然语言处理的基础,算法岗笔试出现率很高。
4. 业务场景与工程实现题
4.1 推荐场景:从召回到排序的完整链路
爱奇艺的业务核心是视频,所以笔试中必然出现推荐相关题目。有一道简答题问的是“请简述推荐系统的召回和排序阶段各自的作用及常用方法”。这道题看似开放,实则有固定得分点。
召回阶段的目标是从海量视频库中快速筛选出几百个候选,要求速度快、覆盖率广。常用方法包括:基于物品的协同过滤(ItemCF)、基于用户的协同过滤(UserCF)、双塔模型向量召回、Item2Vec等。这里有个容易混淆的点:协同过滤和向量召回的本质区别在于,前者是显式地利用“物品共现”或“用户行为相似度”来计算,后者是把用户和物品分别映射到低维向量空间,用内积或余弦相似度做近似最近邻检索。
排序阶段的目标是对候选进行精细打分,常用模型从LR、FM到GBDT、DeepFM、DIN不等。笔试中如果问“排序模型为什么用GBDT而不是LR”,得分点是GBDT能自动学习特征组合、对非线性关系拟合更强;缺点是树模型不擅长处理高维稀疏特征,所以业界常把LR和GBDT结合起来,用GBDT做特征工程,再把结果输入LR或DNN。
如果题目进一步追问“如何评估推荐效果”,除了离线常用的AUC、GAUC、Hit Rate@K之外,还要提线上AB实验。这是校招笔试的加分项,说明你不只会跑模型,还懂业务闭环。
4.2 视频业务中的算法落地:画质评估与内容理解
爱奇艺第二场还有一道场景题让我印象很深,大致是:如何在不依赖原始参考视频的情况下,自动评估用户上传视频的画质?这就是典型的无参考图像/视频质量评估问题。
传统的全参考指标如PSNR、SSIM,需要原始视频做对比,但用户上传的视频往往没有原始版本,所以必须用无参考评估方法。工程上常见的思路有两类:一是提取视频的失真特征,比如模糊度、块效应、噪声水平,再训练回归模型预测主观评分;二是用深度神经网络直接端到端学习质量映射,输入视频帧,输出质量分数。
这题在实际业务中很有价值,因为视频平台每天有大量UGC内容上传,如果画质过差会直接影响用户体验。笔试中只要你能说出“全参考 vs 无参考”的区别,再结合业务场景给出合理方案,基本就能拿高分。答得差的一般是上来就写PSNR,完全不考虑“没有原始视频”这个前提。
另一个相关场景是视频内容审核:如何用算法自动识别视频中的违规内容?这类题通常围绕图像分类、目标检测、音频审核、文本审核等多模态技术展开,还会涉及模型置信度阈值设置和人工审核兜底策略。答题时要体现“算法+人工”的闭环思路,而不是只谈模型结构。
5. 常见问题与备赛经验实录
5.1 笔试环境与代码提交的坑
在线笔试题和本地刷题有个很大的区别:提交代码不看你本地运行结果,而是按照题目预先埋好的用例判分。很多人在本地IDE跑得好好的,一提交就“通过率0%”,大概率不是算法错了,而是输入输出格式问题。
我在这里栽过一次,后来总结了一个必查清单:
- 确认是否使用标准输入输出(Java的
Scanner或BufferedReader,Python的input());如果题目说“多组输入”,要看到EOF才停止。 - 注意输出格式,比如行末是否需要空格、浮点数保留几位小数。有一次我因为输出多了个空格,直接判错。
- 数组下标是否越界,尤其是KMP、DP这类需要访问
i-1的算法,边界条件必须单独处理。 - 是否误用了递归导致栈溢出。在线笔试环境通常限制递归深度,深度超1e5的优先考虑改成迭代。
- 代码中不要有调试输出,比如
System.out.println("debug"),它会被当成正式输出的一部分。
还有一个环境细节:爱奇艺的在线编辑器不支持一些IDE傻瓜功能,比如自动补全和代码格式化。建议提前在牛客网上用在线OJ练手,熟悉“没有自动补全”的裸写环境。手写代码的速度和质量,是笔试能不能拿高分的隐形分水岭。
5.2 时间分配与检查策略
我个人的做题顺序建议是:先花2分钟扫一遍所有题目,对难度有个预判;然后按“选择题 → 简答题 → 编程题”的顺序推进。编程题里先做最有把握的那道,哪怕它分值少,先拿稳再做难题,心态会稳很多。
如果编程题做到一半卡住,不要死磕超过15分钟。先把思路和关键代码写在纸上或者注释里,然后去做后面的题目,回头再补。在线笔试系统通常按最终提交的代码判分,部分题还有“部分通过”的概念,哪怕只能通过小数据,也要提交,有分总比空着强。
选择题检查时要特别注意“下列说法错误的是”这类反向提问,我见过太多人在这种题上踩坑:明明会做,结果因为没看清“错误”两个字,选成了正确选项。建议读题时把“错误”“不正确”“不包括”这些关键词圈出来,或者直接在草稿纸上写个大写的“错”字提醒自己。
5.3 复盘方法:把笔试变成能力提升的抓手
笔试结束不等于事情结束,复盘比考试本身更重要。我是这样做的:考完当天趁记忆还热乎,把所有题目和选项尽可能完整地回忆出来,整理成一份错题集。一个星期后再重做一遍,重点看那些“当时不会但看答案秒懂”的题,因为它们暴露的是知识盲区,而不是能力问题。
复盘时不要只对答案,要把每个题背后的知识点串起来。比如KMP的next数组和“字符串匹配”是一个知识簇,可以连带复习Sunday算法、RK算法;快速选择和快排是一个知识簇,可以连带复习堆排序、归并排序的复杂度分析。当你形成这种“题→知识点簇”的映射,刷题的效率会高很多。
另外,我会把错题按“概念模糊”“计算错误”“思路完全没方向”三个等级分类。概念模糊的靠重新看书解决,计算错误的靠刷题提高敏感度,思路没方向的题往往代表某个专题(比如动态规划、概率统计)没有建立系统框架,需要专门拉出来补。
5.4 关于“每天刷多少题”的建议
很多准备校招的同学喜欢问“每天刷几道题才算够”。我的看法是,数量不是关键,关键是“有没有真的想明白”。如果一道题你只是看了一遍答案,觉得懂了,那考试时大概率还是不会写。真正的懂,是能不看答案,在15分钟内把代码完整写出来,并且能说出每一步为什么这么写。
我建议把刷题节奏分成两个阶段。第一个阶段按专题刷,比如这周只刷动态规划,下周只刷字符串匹配,目标是建立每个专题的框架;第二个阶段做混合刷题,每天抽几道不同知识点的题,模拟笔试的随机出题感觉。爱奇艺这套第二场笔试题,非常适合作为第二阶段的开篇练手卷,因为它覆盖的内容足够杂,能帮你快速检验哪块知识还是漏洞。
最后再说一个实际技巧:碰到算法题时,先用一句话写出你的算法思路和复杂度,再动手写代码。比如“这题用动态规划,定义dp[i]为以第i个元素结尾的最长递增子序列长度,时间复杂度O(n^2),空间O(n)”。这个过程能帮你理清思路,也能在代码写不完时向面试官/复盘时展示你的思维过程。笔试虽然是机器判分,但这个习惯对之后的面试手写代码非常有帮助。