ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

深信服算法岗笔试复盘:从贪心到KMP,四道编程题全解析

深信服算法岗笔试复盘:从贪心到KMP,四道编程题全解析 2023年秋招那阵子我投了深信服算法岗刷完简历之后收到了笔试通知。说实话深信服的笔试在网络安全和云计算公司里算比较有代表性的——既考通用算法功底又带一点安全/网络行业偏好题量和时间卡的都挺紧。这篇东西我拖了很久才写因为当时考完确实有不少题值得复盘。如果你准备投深信服或者其他安全/云计算方向的算法岗这篇能帮你省不少力气。先交代一下基本信息我参加的是2023年9月那批集中笔试平台是牛客网总时长120分钟题型分了三块——不定项选择题、编程题、简答题。编程题一共4道支持的语言有C、Java、Python但我建议能用C就用C后面我会解释为什么。整体难度中等偏上热门考点集中在贪心、动态规划、字符串匹配、图论基础以及少量机器学习和网络协议常识。1. 投递背景与岗位认知1.1 为什么深信服算法岗值得认真准备在投之前你需要搞清楚一件事深信服不是传统互联网公司它是做企业级网络安全、云计算和IT基础设施的。这意味着它的算法岗不像字节、阿里那样核心是推荐/搜索/广告而是更偏向安全检测、网络优化、分布式存储、AI安全这些方向。所以笔试题目虽然以基础算法为主但简答题和部分选择题会明显偏网络、偏系统、偏安全。我当时就吃了这个亏——纯刷LeetCode是远远不够的。岗位方向大致分两类一类是安全算法岗做恶意流量识别、威胁情报、UEBA用户行为分析、恶意样本检测这些另一类是云计算/网络算法岗做SD-WAN路径优化、超融合资源调度、云桌面传输优化、分布式存储纠删码等。你投的志愿方向不同简答题的侧重会有差异。我投的是安全算法方向所以简答题考了攻击检测相关的机器学习模型设计后面细说。1.2 岗位应聘的整体判断另外想提醒一句深信服的招聘流程比较长笔试只是第一关后面还有技术面、HR面部分岗位有加面。笔试成绩是筛选简历后进入面试的重要依据尤其是编程题据说会直接拉出来看你的代码规范性和AC率。所以笔试的优先级很高不要抱着随便写写反正有面试的心态。笔试通过后大概一周内会收到面试通知面试技术面会有两道左右的手撕算法和笔试风格挺一致的基本也是贪心、DP、字符串、图论这些范围。所以笔试题复盘好了面试手撕也能顺手很多。我写这篇文章本质上就是想把这一整套备考逻辑讲透。2. 编程题复盘四道题完整拆解2.1 贪心算法会议室安排问题第一道是经典题会议室安排。题目大概是这样给定n个会议的开始时间和结束时间每个会议室同一时刻只能安排一个会议问最少需要多少个会议室才能容纳所有会议。这道题在LeetCode上是253题力扣会员题但是校招笔试特别喜欢出。核心思路是贪心最小堆先把所有会议按开始时间排序然后用一个小顶堆维护当前正在开的会议的结束时间。每来一个新会议先看堆顶的会议是否已经结束如果堆顶结束时间小于等于当前会议开始时间说明可以复用这个会议室把堆顶弹出。然后无论如何都把当前会议的结束时间压入堆中。最后堆的大小就是所需的最少会议室数量。#include bits/stdc.h using namespace std; int main() { int n; cin n; vectorpairint, int meetings(n); for (int i 0; i n; i) { cin meetings[i].first meetings[i].second; } sort(meetings.begin(), meetings.end()); priority_queueint, vectorint, greaterint pq; for (auto m : meetings) { if (!pq.empty() pq.top() m.first) { pq.pop(); } pq.push(m.second); } cout pq.size() endl; return 0; }时间复杂度O(nlogn)排序是瓶颈空间复杂度O(n)就是堆的大小。笔试时注意输入格式题目给的是一行两个整数用空格隔开千万别用错了。这道题还有一个变体是最多能参加多少场会议那个用贪心选最早结束时间就行不需要堆按结束时间排序后遍历一遍即可。两种考法最好都准备我笔试那次考的是最少会议室但面试手撕就可能变成最多场次不要只背一种。2.2 KMP算法next数组的计算与实现第二道题直接给了模式串p abacaba要求写出next数组并实现KMP匹配逻辑。这种题两个考点一是next数组的定义要搞清楚二是要能写出线性复杂度的匹配代码。我在这里必须强调一个容易翻车的地方next数组在教材里有两种主流定义不同刷题平台、不同教材用的定义不一样。一种是严蔚敏《数据结构》风格的next[1] 0next[i]表示前i个字符组成的子串中最长相等前后缀的长度加1另一种是竞赛和LeetCode风格的next[i]直接表示前i1个字符下标从0开始的最长相等前后缀长度。笔试的时候一定要先看题目给出的定义再作答。按最长相等前后缀长度这种定义下标从1开始p abacaba的next数组是i1234567p[i]abacabanext[i]0010123计算方法是next[i]是p[1..i]这个前缀的最长相同前后缀长度并且前缀和后缀都不能是完整的子串本身。比如i3时子串是aba最长相同前后缀是a长度1所以next[3]1。i6时子串是abacab前缀ab和后缀ab匹配长度2所以next[6]2。i7时子串是abacaba最长相同前后缀是aba长度3所以next[7]3。如果用严蔚敏版定义结果就是next[1]0然后后面的每个值等于最长相等前后缀长度加1变成0 1 2 1 2 3 4。两个版本考的都有做题前一定要看清楚题目给的是哪一种。求next数组的代码竞赛版下标从0开始也很简单vectorint getNext(const string p) { int m p.size(); vectorint next(m, 0); for (int i 1, j 0; i m; i) { while (j 0 p[i] ! p[j]) { j next[j - 1]; } if (p[i] p[j]) { j; } next[i] j; } return next; }匹配部分就是当文本串和模式串失配时模式串下标j跳到next[j-1]继续比较。KMP的核心价值在于文本串指针不回退匹配总复杂度是O(nm)笔试一般会要求分析这个复杂度。这道题还有延伸考法给一个长文本和一个模式串问你模式串在文本中出现多少次、首次出现位置。有的题目会要求你手算next数组然后在答题框里填答案有的直接要求写完整匹配代码。我这次遇到的是后者要求手写KMP完整代码所以一定要把getNext和匹配函数分开写结构清晰而且变量名要起得有意义。2.3 动态规划最长上升子序列与变体第三题是动态规划最长上升子序列LIS。题目描述非常标准给定一个整数数组求最长严格递增子序列的长度。输入没有给数据范围但从样例看n应该在10^5级别所以O(n^2)的常规DP会超时必须用贪心二分的O(nlogn)做法。这个做法的核心是维护一个数组dd[i]表示长度为i的最长上升子序列末尾元素的最小值。遍历原数组时对每个元素x在d中用二分查找找到第一个大于等于x的位置pos然后把d[pos]更新为x。如果pos超出了当前d的长度说明我们可以构造更长的上升子序列就扩展一位。最终答案就是d的长度。#include bits/stdc.h using namespace std; int main() { int n; cin n; vectorint nums(n); for (int i 0; i n; i) cin nums[i]; vectorint d; for (int x : nums) { auto it lower_bound(d.begin(), d.end(), x); if (it d.end()) { d.push_back(x); } else { *it x; } } cout d.size() endl; return 0; }时间复杂度O(nlogn)空间复杂度O(n)。这里有个易错点是lower_bound和upper_bound的选择严格递增用lower_bound第一个大于等于x的位置非严格递增用upper_bound第一个大于x的位置。很多人在这一点上翻车。题目后面还有一个小问要求输出具体的上升子序列而不是长度。这个变体需要在更新d的同时记录每个位置元素在原数组中的下标然后倒推出序列。我当时时间不够只写了长度但建议备考时把这个变体也练一下面试很容易追问。2.4 多源BFS感染蔓延模拟题第四题是一道带业务场景的图论题特别有深信服的风格。大概意思是一个n行m列的网格里0表示正常主机1表示已被感染的主机每个单位时间感染会向上下左右四个方向扩散一格问多长时间能把整个网格全部感染如果永远感染不完就返回-1。这题本质是腐烂的橘子LeetCode 994用多源BFS解决。思路是先把所有初始感染主机入队同时统计正常主机数量。BFS每一层代表一个单位时间每感染一个正常主机就把计数减1。最后如果正常主机数量不为0说明有的主机被墙隔开永远感染不到返回-1否则返回BFS的层数减去1。#include bits/stdc.h using namespace std; int main() { int n, m; cin n m; vectorvectorint grid(n, vectorint(m)); queuepairint, int q; int fresh 0; for (int i 0; i n; i) { for (int j 0; j m; j) { cin grid[i][j]; if (grid[i][j] 1) q.push({i, j}); else if (grid[i][j] 0) fresh; } } vectorint dx {-1, 1, 0, 0}; vectorint dy {0, 0, -1, 1}; int steps 0; while (!q.empty()) { int sz q.size(); bool changed false; for (int k 0; k sz; k) { auto [x, y] q.front(); q.pop(); for (int t 0; t 4; t) { int nx x dx[t]; int ny y dy[t]; if (nx 0 nx n ny 0 ny m grid[nx][ny] 0) { grid[nx][ny] 1; --fresh; q.push({nx, ny}); changed true; } } } if (changed) steps; } if (fresh ! 0) cout -1 endl; else cout steps endl; return 0; }这道题如果最后返回-1那部分忘了写就丢了整道题的分数非常可惜。多源BFS还有一个变体是从多个出口同时出发找到最近出口之类的题一般配合dist数组做状态记录。考虑到深信服安全业务的背景这类扩散传播的题目出现概率不低值得多练几个类似题。3. 选择题与基础题考点3.1 数据结构与经典算法大题选择题基本是30道不定项覆盖了数据结构、算法、机器学习、网络协议、操作系统五块。不定项的选择题比单选难在容易漏选多选少选都扣分所以拿不准的就不要选。我印象比较深的有几类排序算法的时间复杂度和稳定性对比、KMP next数组手算、快速幂的复杂度、贪心算法的适用场景判断、粒子群算法的原理、聚类算法的基本概念、Dijkstra算法的适用条件。你列出的那些热词比如排序算法堆排序算法快速幂算法C粒子群算法原理KMP算法聚类算法Dijkstra算法确实都是当年的高频考点。排序这块一定要把这张表背熟排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性冒泡排序O(n²)O(n²)O(1)稳定选择排序O(n²)O(n²)O(1)不稳定插入排序O(n²)O(n²)O(1)稳定快速排序O(nlogn)O(n²)O(logn)不稳定归并排序O(nlogn)O(nlogn)O(n)稳定堆排序O(nlogn)O(nlogn)O(1)不稳定快排最坏情况是每次划分都极度不平衡时发生的虽然实际概率低但理论题就是要考这个点。另外快速幂一定要会深信服的笔试里出现过多次它的本质是把指数二进制展开把O(n)的乘法变成O(logn)。粒子群算法PSO当时考的是基本原理问你速度更新公式里那几个参数分别代表什么比如惯性权重、个体学习因子、群体学习因子。这个和公司业务有关深信服有做安全策略优化、路径寻优之类的工作启发式算法是他们的常用工具。备考时至少要把粒子群、遗传算法、模拟退火这三类智能优化算法的核心思想搞清楚。3.2 机器学习与深度学习基础机器学习这块考的不难但很杂。我记得的有K-Means的优缺点、KNN的三大要素距离度量、K值选择、分类决策规则、Bagging和Boosting的区别、过拟合的解决手段、交叉熵损失函数和均方误差的适用场景。深度学习相关的考了Batch Normalization的作用、ReLU激活函数为什么能缓解梯度消失、CNN和RNN的适用场景。如果你是非科班转算法这一部分容易成为短板。建议至少把李航《统计学习方法》前几章感知机、KNN、朴素贝叶斯、决策树、SVM里的经典结论背一遍再补一下深度学习的基础概念。性价比最高的还是从机器学习面试题集入手比如什么是过拟合怎么解决Bagging和Boosting的区别这种每年必考几乎躲不掉。3.3 网络与安全基础常识网络协议和操作系统也考了几道。TCP三次握手四次挥手、DNS解析过程、ARP协议的作用、进程和线程的区别、死锁的四个必要条件。这些是计算机基础里的常客不展开说了但如果你时间紧建议优先把TCP/UDP区别、HTTP/HTTPS区别、进程线程区别这三组背熟出题概率最高。安全方向的选择题会有一些零基础同学完全没见过的名词比如防火墙、IDS/IPS、EDR之间的区别零信任、沙箱、恶意代码检测这些概念。你可以在网上搜一下深信服安全产品体系了解一下他们有哪些产品线比如上网行为管理、终端检测响应、云安全、态势感知等。就算选择题没直接考简答题和面试也会用到的。4. 算法岗笔试背后的业务联系4.1 从公司视角看为什么考这些题很多同学不理解为什么深信服笔试要和互联网大厂一样考算法题这其实是校招筛选的通用逻辑算法题考察的不是背书能力而是建模能力、边界条件处理能力、代码规范性和时间空间复杂度意识。深信服的业务无论是做SD-WAN链路质量监控还是做超融合的资源调度底层都会用到图算法、动态规划、二分查找这些基础。举个例子SD-WAN的核心功能是智能选路哪个链路质量好、延迟低、带宽充裕就把流量从哪个链路发出去。这本质上是一个加权路径选择问题Dijkstra和相关的图算法就是基础。超融合场景里的虚拟机调度、资源分配又很像背包问题和任务调度问题贪心、DP都是常规工具。云桌面场景里图像压缩、缓存优化、协议传输背后的算法一样离不开字符串匹配和动态规划。所以笔试考察的这些算法看起来是八股其实都是他们日常开发里真正在用的东西。我写代码的时候一直在提醒自己这不只是在刷题而是在模拟未来工作中的一个模块。4.2 安全算法方向与AI的结合安全算法方向的简答题问了怎么用机器学习识别恶意流量或恶意样本。这题没有标准答案但你要能说清楚一套完整的建模流程数据收集、特征工程、模型选择、评估上线。我当时答的是基于网络流量元数据的恶意流量分类特征用了五元组、包长统计、协议类型、会话时长、上下行流量比模型选了GBDT。延伸一点可以提到用深度学习方法做端到端的流量分类比如用CNN处理原始报文前几百个字节或者用Transformer做序列特征建模。另外一个热门方向是UEBA用户行为异常检测。这个通常是无监督问题因为恶意行为的标签很难获取一般先用K-Means或孤立森林做异常检测再用规则或者可解释模型做告警收敛。类似的题还包括恶意域名检测基于DNS日志、DGA域名识别基于字符特征分类器、钓鱼邮件检测基于NLP。这些题目准备的时候至少要能说出特征模型评估的完整链条不要只答一个用深度学习。4.3 云计算方向的延伸思考如果你投的是云计算/网络方向的算法岗建议多准备分布式系统相关的知识点。比如一致性哈希在缓存负载均衡中的作用、数据去重中的哈希算法、纠删码和副本策略的取舍、vGPU细粒度切分的资源分配策略。对于这几块哪怕笔试没有具体考面试也很有可能追问。我对分布式了解不算很深但至少把一致性哈希的原理、虚拟节点的作用、虚拟化中GPU切分的概念过了一遍面试时能说出个大概就已经比大多数裸考的竞争者好很多了。总之算法基础是敲门砖但对公司业务的了解会让你在简答题和后续面试环节明显拉开差距。你不需要真的用过他们的产品但至少要明白他们做的是哪几件事、核心产品的技术点大概是什么。5. 实战避坑与备考建议5.1 在线笔试环境与细节我是在牛客网笔试的考前一定要提前做两件事一是测试摄像头和麦克风深信服这种企业级公司对笔试纪律管得很严双机位是常态副机位没开会被判定作弊二是确认浏览器兼容性牛客网推荐用Chrome或者Edge用旧版浏览器容易出现代码编辑器卡死的情况。我那次就有同学因为浏览器问题导致代码区渲染故障折腾了十分钟。另外笔试过程中会时不时有人脸检测弹窗需要你保持面部在摄像头范围内。中途离开太久可能直接判定成绩无效这些细节虽然不涉及技术但真的会影响结果。血的教训是提前把桌面整理干净不要有纸质资料不然被拍到写代码时旁边有纸真的说不清楚。代码输入输出格式也是大坑。深信服这次笔试是ACM模式和牛客网上刷题一样所有输入输出都得自己处理。有些同学平时在LeetCode上习惯了函数输入输出一到ACM模式就懵了。考前建议在牛客网或者洛谷上练几道需要手写完整main函数的题尤其是字符串输入里可能带逗号、括号、引号的情况都要会处理。5.2 时间分配与做题顺序120分钟的时间我个人的分配方案是先通读所有题目选择题控制在40分钟以内编程题每道15到20分钟最后留10分钟检查。把最简单、最有把握的编程题放在第一个做保证分数落袋为安。我先写了会议室贪心和KMP再做LIS最后做多源BFS这个顺序比较合理。千万别在第一道题上死磕。如果一道题超过25分钟还没有AC果断放弃去写下一道最后回来再补。因为笔试分数是按用例通过百分比算的部分AC也有分空着就是零分。我记得LIS那道题第一版用O(n²)的DP提交之后超时我赶紧切到下一题做完多源BFS之后回来改成O(nlogn)才过。所以做题策略真的很重要。调试技术上也有一点提示笔试时可以开多个代码页但最终提交的一定是完整版代码注释掉的测试输出最好删掉。有些人习惯用printf/cout打日志调bug提交前忘记注释直接导致输出格式错误整道题被判0分非常冤。我每次都会提交前检查一遍输出是否只有答案没有额外调试信息。5.3 考前一周的具体准备考前一周的建议从这几个方向准备每天保证刷4到6道题优先覆盖高频考点贪心、动态规划、字符串、二叉树、图论、排序其次把机器学习经典面试题过一遍不需要会推导公式但要能说清楚原理最后把深信服的主要产品线和技术方向大致浏览一遍至少要知道他们做SD-WAN、超融合、云桌面、EDR这些并且能对应到具体技术点。刷题的时候我用的是牛客网的历年真题库和LeetCode的Hot 100按标签分类刷而不是按题目顺序刷。比如贪心标签下的题目一口气做5道做完总结规律动态规划标签以背包、LIS、编辑距离、区间DP为主每个类型吃透一题就够了。这套方法保证在短时间内覆盖面试高频知识点。还有一条非常重要的经验考完后立刻记录自己答了什么、哪些题目没AC方便面试前针对性复习。我笔试完当天就把四道题的题目和代码整理到了笔记里后来技术面被问到了KMP的next数组怎么计算我直接把这个文档翻出来重新讲了一遍非常从容。这比临时抱佛脚要有效得多。最后再分享一个小细节深信服笔试的简答题是不允许跳过的你必须写在规定的文本框里才能交卷。所以时间分配上不要只盯着编程题简答题至少要留15分钟来写。能写多少写多少哪怕写思路也能拿一部分分但空着就真的什么都没了。我当时的解法思路写得很细连特征工程和评估指标都写了虽然在考场上比较赶但这个习惯在后续面试提问时帮我省了很大的力。
返回列表