[CP] 刷题小记
简单记录一下刷题过程中学到的 trick 以及容易犯错的地方。
只是写给自己看的,有些地方因为没有附带题目背景或条件,看起来会很突兀。
提取英文字母的编号
char c = 'a'; int id = c & 31;
不区分大小写。a / A 返回 1,z / Z 返回 26,以此类推。
双指针分段 + 哨兵
以前写分段都要特别处理末尾一段的情况,非常麻烦。今天突然意识到,往末尾加一个哨兵就能简化这一流程。
寻找最小值和次小值
if a[x] < minVal: subMinVal = minVal minVal = a[x] else: subMinVal = min(subMinVal, a[x])
当找到新的最小值时,旧的最小值会成为次小值——这一步总是忘记。
需要注意的是,subMinVal = minVal
仅适用于一般的情况,如当前有多组数据,需要找到每一组数据的最小值以及最小(全局)的次小值(见 3143. Maximum Points Inside the Square),需要改成 subMinVal = min(subMinVal, a[x])
,因为全局次小值有可能比当前数据对应的最小值还要小。
子数组的数量
假设数组长度为
总是写成
滑动窗口统计子数组数量
假设滑动窗口左右指针为 L、R,目标子数组满足的条件为 R - L + 1
),然后用总的子数组数量作差得到答案,也可以直接统计满足 L
来统计。
不过个人还是比较习惯前一种写法,因为比较贴合 “窗口” 的概念。两种写法在时间复杂度上并没有区别。
这里的 “单调性”,准确地来说是 “单峰性”,只不过用单调性更形象一点。因为存在单峰性的数据一般也能用二分法进行处理,那么,能用双指针解决的题目是否也一定能用二分解决呢?这是一个有趣的猜想。
不管怎么说,双指针的时间复杂度更优,而且所需的代码量多数情况下比二分要少,所以我选择双指针。
特判窗口长度为零的情况
滑动窗口类题目中,如果可能出现窗口长度为零的情况,则需要对其特判,否则可能出现匪夷所思的结果。对于统计子数组个数的题目,并不会造成多大影响,然而对于最值统计类题目,就会取到极端值。
滑动窗口不需要前缀和
滑动窗口类题目除非需要查询与窗口端点无关的区间,一般不需要维护前缀和数组,因为区间和都能在窗口移动时进行动态更新。
这一点不仅仅适用于前缀和,如果每次只用到和窗口有关的数据,那么便可以“增量”update/check,不需要重新计算所有数据。
今天做了一天的滑动窗口,大概有二十几道题目,难度分从 1300 到 2500 不等,总体上比做 cf 轻松愉快多了,也没有出现必须看题解才能做出来的题目,不过从题解中学到了许多优化和减少出错的技巧(也就是上面这些东西)。此外,我感觉最重要的是要理解代码每一步的意图,什么时候要特判,什么时候不用,边界条件怎么设置等等,而不是套模板或套以前做过的题目的答案,否则就会写出奇形怪状、难以阅读且出错率高的代码。
std::array
不会自动初始化
以前一直以为它可以自动初始化,今天做题别出心裁把 vector 换成 array,一直报错,打印数据发现全是随机值,这才知道 array 的行为和普通数组一样,如果不提供初值,不会自动初始化!!
定长滑动窗口出窗口
出窗口是 i-len+1
,不是 i
!
左闭右开二分查找
区间上界应取 MAX + 1
!
需要输出原序号
有些题目涉及排序等导致原输入顺序改变的操作,如果题目要求输出原输入顺序对应的序号,则需要储存这一信息。可以通过在输入数据域中附加序号信息,或使用索引数组实现,如果使用索引数组,所有对数组的操作必须通过索引数组进行。
今天做一道 kruskal 的题目 debug 好久后才发现犯了这个愚蠢的错误……
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理