目录
Hello朋友们😋,我是秋刀鱼🐟,一只活跃于Java区与算法区的新人博主~
![]()
欢迎大家加入高校算法学习社区🏰: https://bbs.csdn.net/forums/Suanfa,社区里大佬云集,大家互相交流学习!
今天给大家带来LeetCode 289场单周赛的题目解析,并分享一下我解题时的思考过程与犯下的错误。如果觉得还不错的话务必三连支持一下博主哦😘
🎉🎉主页:秋刀鱼与猫🎉🎉 🎉🎉期待你的支持与关注~🎉🎉
提示:
1 <= s.length <= 1002 <= k <= 100s仅由数字(0-9)组成。
字符串中的每 k 个整数进行加运算可以为一个新的整数,不足 k 个整数的一组在末尾同样可以合并。
实现思路可以使用递归解决,第一步判断字符串长度,如果长度小于 k 直接返回,否则执行K个一组的拼接操作并进入下一层递归。
思路还算单这里不过多赘述,大家可以自己动手写一写哦。
class Solution { public String digitSum(String s, int k) { int len = s.length(); if (len <= k) { return s; } StringBuilder str = new StringBuilder(); int i = 0; for (; i + k < len; i += k) { int val = 0; for (int j = i; j < i + k; ++j) { val += s.charAt(j) - '0'; } str.append(val); } int val = 0; // 处理最后一组的情况 for (; i < len; ++i) { val += s.charAt(i) - '0'; } str.append(val); return digitSum(str.toString(), k); } }
题目中说明,每一轮中能够完成 2 个或 3 个同样难度级别的任务,稍加分析可以得知,无法被 2 或 3 整除的任务数量一定无法完成。所有的正实数中,无法被 2 或 3 整除的数只有 1 ,因此当某个任务数量为 1 时直接返回 -1 。
对于完成任务需要的最少轮数,运用到贪心的思想:每次尽可能多地完成任务,也就是尽可能在一轮中完成 3 次任务。但是这里需要留意:当
任务数%3 == 1时,最终可能会剩下 4 个任务,因为不能单独完成 1 个任务因此 4 个任务要分成 2、2 来完成。最终遍历所有任务返回完成任务的轮数之和即是结果值。
class Solution { public int minimumRounds(int[] tasks) { // 存放每个任务的数量 Map<Integer, Integer> taskMap = new HashMap<>(); for (int val : tasks) { taskMap.put(val, taskMap.getOrDefault(val, 0) + 1); } int ans = 0; for (Map.Entry<Integer, Integer> entry : taskMap.entrySet()) { int val = entry.getValue(); // 任务数量为 1 代表无法完成,直接返回 -1 if (val == 1) { return -1; } int red = val % 3; if (red == 0) { ans += val / 3; } else if (red == 1) { ans += (val - 4) / 3 + 2; }else{ ans += val / 3 + 1; } } return ans; } }
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 1051 <= m * n <= 1051 <= grid[i][j] <= 1000
叨叨两句
这道题目我在周赛中挺多的时间,并不是说题目有多难,而是没有注意到 5 因子的数量可能少于 2 因子的数量,在这一点上耽误了太多的时间,也导致最后一题没有时间AC。虽然最后还是A了这道题,但是代码还是又长又臭,心里很不是滋味。
![]()
前置知识
相信很多朋友都有遇到过乘数的尾数 0 这一类的问题,但是我还是想在这里简单阐述一下。
对于任意两对数的乘法运算,产生的尾数 0 是来源于 2、5 这两个因子相乘,如果没有这两个因子的乘法运算式则无法产生尾数 0 的。
举个栗子:大家都知道 4 ⋅ 25 = 100 4\cdot25 = 100 4⋅25=100, 100 100 100 后有两个尾数 0 。之所以得到两个尾数 0 是因为 25 25 25 被拆分为 5 ⋅ 5 5\cdot5 5⋅5 提供了两个 5 因子,而 4 被分解为 2 ⋅ 2 2\cdot2 2⋅2 提供两个 2 因子。这些因子凑成了两对 2 ⋅ 5 2\cdot5 2⋅5 的式子因此提供了两个尾数0。
因此乘法运算中尾数 0 的个数等于拆分每一个乘数得到的 5、2 因子数量的最小值。
问题分析
题目中要求找到尾数 0 位数最多的一条转角路径,转角路径指的是一条最多改变一次方向的直线。既然是最多改变一次方向,同样可以不改变方向,但是这一点可以不在讨论的范畴之内,因为尽可能多地走更多的格子才有可能获取更大位数的位数0,这点基于贪心的思想。
既然每一条路径都想要尽可能多地走几步,那么符合要求的路径一定满足下面的要求:
- 转动一次
- 一直移动直到边界停止
既然路劲一定会转动一次,那么只需要枚举出所有点的转动情况,求出尾数0位数的最大值即可,例如枚举到下图中黄色表示的格子为转动点:
那么在点的转动一定存在下面四种移动情况:
路径求值
存在也仅存在这四种路径情况,那么现在只需要枚举这四种路径情况上的值,找到每一条路径上的值 5 因子与 2 因子数量的最小值,即是这一条路径的尾数 0 的位数。为了方便计算,我将原数组中每个数值转换为其 5 因子的个数,并定义了一个新的数组存储每一个值的 2 因子数目。
因为害怕超时,在我的代码中使用了前缀和的思想来求因子数目。将一条路径分为横、竖两条,通过预先处理 5 因子、2 因子的前缀和数组的方式来快速获取因子数目,再将两个因子数目进行一个比对,最小值即是该路径尾数 0数量。
class Solution { int n, m; // 获取 base 因子的数量 public int check(int val, int base) { if (val % base != 0) { return 0; } int ret = 0; while (val % base == 0) { ++ret; val /= base; } return ret; } // 处理前缀和数组 public void build(int[][] preSumLine, int[][] preSumTop, int[][] grid) { for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { preSumLine[i][j + 1] = grid[i][j]; preSumLine[i][j + 1] += preSumLine[i][j]; preSumTop[j][i + 1] = grid[i][j]; preSumTop[j][i + 1] += preSumTop[j][i]; } } } public int maxTrailingZeros(int[][] grid) { n = grid.length; m = grid[0].length; int[][] bygird = new int[n][m]; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { int cur = grid[i][j]; int num1 = check(cur, 5); int num2 = check(cur, 2); grid[i][j] = num1; bygird[i][j] = num2; } } // 4 个前缀和数组 int[][] preSumLine = new int[n][m + 1]; int[][] preSumTop = new int[m][n + 1]; int[][] presSumLine2 = new int[n][m + 1]; int[][] preSUmTop2 = new int[m][n + 1]; build(preSumLine, preSumTop, grid); build(presSumLine2, preSUmTop2, bygird); int ans = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { // 判断 4 条路径的逻辑 int left = preSumLine[i][j + 1]; int right = preSumLine[i][m] - preSumLine[i][j]; int left2 = presSumLine2[i][j + 1]; int right2 = presSumLine2[i][m] - presSumLine2[i][j]; int top = preSumTop[j][i]; int bottom = preSumTop[j][n] - preSumTop[j][i + 1]; int top2 = preSUmTop2[j][i]; int bottom2 = preSUmTop2[j][n] - preSUmTop2[j][i + 1]; int leftTop = Math.min(left + top, left2 + top2); int rightTop = Math.min(right + top, right2 + top2); int leftBottom = Math.min(left + bottom, left2 + bottom2); int rightBottom = Math.min(right + bottom, right2 + bottom2); ans = Math.max(ans, Math.max(leftTop, Math.max(rightTop, Math.max(leftBottom, rightBottom)))); } } return ans; } }
提示:
n == parent.length == s.length1 <= n <= 105- 对所有
i >= 1,0 <= parent[i] <= n - 1均成立parent[0] == -1parent表示一棵有效的树s仅由小写英文字母组成
一道很经典的题目,解题的思路是使用递归来求解。
首先不考虑太多结点,就仅仅考虑一个结点与其子节点的情况:
如果考虑路线经过父节点,那么路线可能会存在下面的两种方式:
现在就这两种路径方式进行讨论:
1、要求相邻的结点要求字符不相同
这点也就是说经过父节点的路径上相邻结点,也就是其左右孩子结点,字符不能与父节点字符相同。如上图所示,左孩子与父节点字符相同,因此任何从左孩子过来的路径都不予考虑。
2、要求题目中要求返回最长的路径
既然经过父节点会存在这两种方式,对于
- 方式一中路径的长度一定是左右孩子有效路径之和,例如上图中的有效路径只是父节点到右孩子的路径有效,左孩子与父节点相同因此其返回的路径无效。
- 方式二中既然要求返回最长的路径,那么继续向上传递的路径同样是左右孩子中有效路径最长的那一条路径。
因为路径一定经过父节点,因此方式一、方式二中的路径都需要加上父节点也就是长度+1。
class Solution { // 保存 父 -> 子 结点的关系 List<Integer>[] sons; int ans = 0; String s; int n; public int dfs(int idx) { int max = 0; // 存放所有有效路径长度 List<Integer> tmp = new ArrayList<>(); // 哨兵元素,便于处理 tmp.add(0); tmp.add(0); if (sons[idx] != null) { for (int son : sons[idx]) { // 返回子节点向上传递的路径长度 int value = dfs(son); // 判断该路径是否有效 if (s.charAt(idx) != s.charAt(son)) { tmp.add(value); max = Math.max(value, max); } } } // 排序便于返回 tmp.sort((a,b)->{ return b - a;}); // 更新最长路径长度 ans = Math.max(ans, 1 + tmp.get(0) + tmp.get(1)); // 返回子节点最长有效路径 return tmp.get(0) + 1; } public int longestPath(int[] parent, String _s) { n = parent.length; sons = new List[n]; this.s = _s; for (int i = 1; i < n; i++) { if (sons[parent[i]] == null) { sons[parent[i]] = new ArrayList<>(); } sons[parent[i]].add(i); } dfs(0); return ans; } }
ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem
前言我们习惯用idea编写、调试代码,在LeetCode上刷题时,如果能够在IDEA编写代码,并且做好代码管理,是一件事半功倍的事情。对于后续复习题目,做笔记也会非常便利。本文目的在于介绍LeetCodeEditor的使用,以及配置工具类,最终目录结构如下:note:放置笔记src:放置代码leetcode.editor.cn:插件LeetCodeEditor自动生成utils:自定义的工具包,可用于自动化输入测试用例,定义题目需要的类(结构体)out:运行测试时自动生成LeetCodeEditorGitHub:https://github.com/shuzijun/leetcode-edit
文章目录问题B:芝华士威士忌和他的小猫咪们代码&注释问题C:愿我的弹雨能熄灭你们的痛苦代码注释问题D:猜糖果游戏代码注释问题E:有趣的次方代码注释问题F:这是一个简单题代码&注释问题G:打印矩阵代码注释问题H:scz的简单考验代码注释问题I:完美区间代码&注释问题J:是狂热的小迷妹一枚吖~代码&注释2022年10月23日周赛ZZULIOJ问题B:芝华士威士忌和他的小猫咪们时间限制:1Sec内存限制:128MB题目描述芝华士威士忌很喜欢带着他的猫咪们一块跑着玩。但是小猫咪们很懒,只有在离他y米以内才愿意和他一块跑。这天他在坐标为x的位置,他想和他的猫咪们一块跑着玩。有n个小猫咪,第i个小猫咪在坐
在IEEE754-2008节"9.2.1Specialvalues"有提到pow(+1,y)is1foranyy(evenaquietNaN)如果没有阅读整个文档,维基百科给出了shortcut:The2008versionoftheIEEE754standardsaysthatpow(1,qNaN)andpow(qNaN,0)shouldbothreturn1sincetheyreturn1whateverelseisusedinsteadofquietNaN.为什么Math.pow(1,NaN)在JavaScript中是NaN?不符合标准吗? 最佳答案
一、题目给你一个整数数组ranks和一个字符数组suit。你有5张扑克牌,第i张牌大小为ranks[i],花色为suits[i]。下述是从好到坏你可能持有的手牌类型:“Flush”:同花,五张相同花色的扑克牌。“ThreeofaKind”:三条,有3张大小相同的扑克牌。“Pair”:对子,两张大小一样的扑克牌。“HighCard”:高牌,五张大小互不相同的扑克牌。请你返回一个字符串,表示给定的5张牌中,你能组成的最好手牌类型。注意:返回的字符串大小写需与题目描述相同。来源:力扣(LeetCode)链接:https://leetcode.cn/problems/best-poker-hand/d
假设,我在Node.js中有一个异步函数,基本上是这样的:varaddAsync=function(first,second,callback){setTimeout(function(){callback(null,first+second);},1*1000);};现在我当然可以以异步方式调用这个函数:addAsync(23,42,function(err,result){console.log(result);//=>65});我想知道的是,您是否可以通过某种方式同步调用此函数。为此,我想要一个包装器函数sync,它基本上执行以下操作:varsync=function(fn,pa
我正在测试的页面有一个按钮,可以将您带到同一站点上的不同页面。单击该按钮后,我想等待该页面加载后再继续。通常,我只会等待该页面上的某些元素加载,但由于我最近更新了nightwatch/selenium,waitForElementPresent()测试已停止工作。在调试问题的过程中,我认为等待新URL加载是有意义的,但我没有看到守夜人的方式来做到这一点。我可以用一个pause()后跟一个assert.urlContains()来硬编码等待,但必须有更好的方法。有什么建议吗?过去的工作:this.waitForElementVisible(runCSS,3000).click(runCS
🍎道阻且长,行则将至。🍓🌻算法,不如说它是一种思考方式🍀算法专栏:👉🏻123一、🌱344.反转字符串题目描述:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组s的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)的额外空间解决这一问题。来源:力扣(LeetCode)难度:简单提示:1s[i]都是ASCII码表中的可打印字符示例1:输入:s=[“h”,“e”,“l”,“l”,“o”]输出:[“o”,“l”,“l”,“e”,“h”]示例2:输入:s=[“H”,“a”,“n”,“n”,“a”,“h”]输出:[“h”,“a”,“n”,“n”,“a”,“H”
数据规模->时间复杂度10^8内容二维数组中的路径问题买卖股票的最佳时机lc62【剑指098】【top100】:不同路径https://leetcode.cn/problems/unique-paths/提示:1题目数据保证答案小于等于2*10^9#方案一:dfs+记忆化classSolution:defuniquePaths(self,m:int,n:int)->int:memo=[[-1]*nfor_inrange(m)]defdfs(i,j):ifi==m-1andj==n-1:return1ifi>=morj>=n:return0ifmemo[i][j]!=-1:returnmemo[
🍎道阻且长,行则将至。🍓🌻算法,不如说它是一种思考方式🍀算法专栏:👉🏻123hash是什么,哈希表为什么叫哈希表?一、🌱454.四数相加II题目描述:给你四个整数数组nums1、nums2、nums3和nums4,数组长度都是n,请你计算有多少个元组(i,j,k,l)能满足:0nums1[i]+nums2[j]+nums3[k]+nums4[l]==0来源:力扣(LeetCode)难度:中等提示:n==nums1.lengthn==nums2.lengthn==nums3.lengthn==nums4.length1-2^28示例1:输入:nums1=[1,2],nums2=[-2,-1],n