LeetCode 20-5 每日一题
上个月没搞完,看这个月能不能坚持下来咯
Day1: 合并有序链表,经典题,写一个空节点当头。
Day2: 无重复字符的最长子串, 又是一道做过的题,而且4个月前的做法和现在的已经不一样了
Day3: 最大子序和,经典dp 维护前面的最大子序和,由于连续,对于当前这个值 要么加到前面,要么重新开一个子序
Day4: 跳跃游戏 II,给一个数组,值代表当前这个点最远能跳到右边多远,问最少几步能跳到终点
线段树区间更新,单点查询(复习下线段树..)
Day5: 验证二叉搜索树, 二叉搜索树中序遍历肯定有序,验证是否有序即可
Day6: 最低票价,线性dp(我自己写了个365*30的), dp[i] = min(dp[i-1]+costs[0],dp[i-7]+costs[1],dp[i-30]+costs[2]);
Day7: 另一个树的子树,裸的匹配是O(n^2)的, 用树hash可以降到O(n) \(hash[rt] = (rt->val + hash[rt->left] * prime[sz[rt->left]] * 31 + hash[rt->right] * prime[sz[rt->right]] * 197)%MOD;\)
Day8:最大正方形
可以用悬线法来做, 定义0为障碍点 悬线为该点向某个方向延伸不碰到障碍点的长度, up代表该点向上悬线的长度 left,right 代表向左右延伸能延伸到那个点
答案就是 $$min(up[i][j],(right[i][j]-left[i][j]+1))^2$$ , 在统计答案时要用上一行的left,right更新这一行的,详情见oiwiki。
Day9: 手写开根号, 牛顿迭代法逼近, 也可以二分做
Day10: 二叉树的最近公共祖先 (n^2 都能过。。。) 预处理每个节点的父亲和深度,然后向上跳就行。
Day11: 快速幂
Day12: 写一个能返回栈内最小元素的栈, 内置一个单调减的栈即可(如果后面的比前面大 不会影响答案)
Day13: 二叉树的层序遍历, dfs求距离
Day14: 只出现一次的数字, 4月一道每日一题的简化版, a^a = 0, 0^a = a, 所以0 异或一遍数组就是答案
Day15: 和为k的子数组, 对前缀和用map存下来, 对于当前位置的前缀和 \(sum_i\), 只需要找到满足 \(sum_i - k = sum_j\) 的 j的个数
Day16: K 个一组翻转链表, 先向后跑k个, 如果存在则翻转这k个,然后直接到k+1个继续
Day17: 课程表 II, 裸的拓扑排序,给出的矩阵就是前序关系
Day18: 乘积最大子数组,记录下前面的最大和最小, 自己写的是根据a[i] 正负分类讨论比较麻烦, 题解中直接对可能取值取max,min 即可
Day19: 验证回文字符串 Ⅱ, dfs判一下就行
Day20: 每个元音包含偶数次的最长子字符串,状压dp,状态是每个字母出现的奇偶性,dp[status]表示 status最早出现的位置(这明明就是dp,感觉leetcode老喜欢上hash上扯)
Day21: 最长回文子串, malchar算法, 字符串开头和每个字符后面插个'#',使其变成奇数串,p[i]表示以i为中心的回文串的长度,然后mx记录一下当前回文串中向右最远的距离,id为它的下标, p[i] = mx > i ? min(p[2*id-1],mx) : 1; 用来更新p[i]
Day22: 从前序与中序遍历序列构造二叉树, 经典题, 对于这种区间处理前段时间看了个文章,可以用半开区间处理, [l,r),这样可以少判断边界条件
Day23: 最小覆盖子串, 双指针,右指针一直往右,直到完成覆盖,左指针才往右,走到第一个不满足覆盖的点更新答案。
Day24: 寻找两个正序数组的中位数, 这题是有点难的不过我之前学过一边, 先考虑实现求两个数组的kth,每次从两个数组中取kth/2,然后把较小的那一半删掉(这段区间肯定在k/th前面), 实现时要注意短区间取完也没有kth/2的情况。
Day25: LRU缓存机制, 原来写的O(n)插入删除给t了, 用set<{time,key}>存一下每个key操作的时间, 插入时如果set的长度为n,则把set的第一个key删了. 每次操作更新key,value,time 和set.
Day26: 寻找重复数, 值域上二分重复的数,判断小于等于中位数的个数和(l,r]值域上的个数, 由于结束条件不好判,最后二分出r-l>=1,枚举判断这三个数.
Day27: 和可被 K 整除的子数组,这题写了一小时还是不会,数论知识不足,根据同余定理, \((sum[i] - sum[j])\%m == 0 \Leftrightarrow sum[i] \% m == sum[j] \% m\), 所以统计一遍前缀和相同的就是答案.(还要注意c++负数求余为负数的坑, 数论的求余都是正数域 要写为 \(a\%m=(a\%m+m)\%m\)
Day28: 字符串解码, 写法还是挺多的,我是用dfs(l,r) 表示拼出字符串l,r的结果,预处理出'['对应']'的位置,转移的时候要么碰到一个数字,把这个数字和对应的括号取掉,然后重复k次(可以用二进制加速)然后把剩下的也处理掉。要么碰到一个字符,直接返回s[l]+dfs(l+1,r)
Day29: 打家劫舍,dp入门题,当前选前面就不能选,当前不选前面选不选都行
Day30: 柱状图中最大的矩形, 这种题很明显的单调栈/队列,问题就在于你如何更新答案,维护单调增栈,对i的栈头t,用h[t]*(i-s.top()-1)更新答案,t是比i高的,且t后面的元素肯定都比t高(否则t会被弹出),t前面比t高的也被弹过了,所以答案是t前面元素+1到i-1的长度乘t的高度. 在所有元素都入栈后也要更新一遍答案,当前栈中元素所对应的i就是n(后面的都比他高了), 实现中可以插一个-1,避免空栈(因为你弹完栈头还要用栈头更新位置,-1的右边就是0). 感觉这样理解还是比较复杂的...
Day31: 对称二叉树, 对中序dfs进行hash, val = val * dep, 然后看dfs序是否回文