文章目录
2427. 公因子的数目
给你两个正整数 a
和 b
,返回 a
和 b
的 公 因子的数目。
如果 x
可以同时整除 a
和 b
,则认为 x
是 a
和 b
的一个 公因子 。
提示:1 <= a, b <= 1000
示例
思路
一看数据范围是1000,直接暴力即可
2428. 沙漏的最大总和
给你一个大小为 m x n
的整数矩阵 grid
。
按以下形式将矩阵的一部分定义为一个 沙漏 :
返回沙漏中元素的 最大 总和。
注意:沙漏无法旋转且必须整个包含在矩阵中。
示例
思路
直接模拟一遍即可
2429. 最小 XOR
给你两个正整数 num1
和 num2
,找出满足下述条件的整数 x
:
-
x
的置位数和num2
相同,且 -
x XOR num1
的值最小
注意 XOR
是按位异或运算。
返回整数 x
。题目保证,对于生成的测试用例, x
是 唯一确定 的。
整数的 置位数 是其二进制表示中 1
的数目。
示例
思路
先求得num2
的二进制表示中1的数目cnt
。然后构造x
,x
的二进制表示中,1的个数也要为cnt
。
则我们需要,把cnt
个1,放到x
的某些二进制位上。
而由于要求x XOR num1
的值最小。那么很明显的,先从高位到低位,看看num1
的二进制表示中,哪些位为1,则把1放到x
的对应位上。这样做异或可以抵消为0。如果已经构造到了x = num1
,此时cnt
个1还有剩余,则需要从低位往高位,把1放到那些为0的位置上去。
再贴一下y总的讲解
如果我们把1放到了num1
中为1的那些位置上,做异或后,相当于是减去了一个数(这个位异或后抵消为0了),比如num1
的第8位是1,如果我们在x
中将第8位置为1,则异或的结果相当于在num1
的基础上减去了一个2^7
。
如果我们把1放到了num1
中为0的那些位置上,做异或后,相当于是加上了一个数。
于是,我们需要将cnt
个1放到对应的位置上,实际就是从若干个数中选择cnt
个数,使得选出来的cnt
个数的和最小。
假设num1
的第1,3,5,8位是1。那么我们就需要从如下的数中选出cnt
个(数据范围是10^9,则一共有30个二进制位)
很明显的,要先选负数,且要从负的最多的数开始选。所以我们要从高位开始,选择num1
中为1的那些位置。当num1
中所有为1的位置都被选完了(负数都被选完了),此时cnt
个数还有剩,那么只能选择正数,从最小的正数开始选,那么就是从低位开始,选择num1
中为0的那些位置。
2430. 对字母串可执行的最大删除数
给你一个仅由小写英文字母组成的字符串 s
。在一步操作中,你可以:
- 删除整个字符串
s
,或者 - 对于满足
1 <= i <= s.length / 2
的任意i
,如果s
中的前i
个字母和接下来的i
个字母相等,删除前i
个字母。
例如,如果 s = "ababc"
,那么在一步操作中,你可以删除 s
的前两个字母得到 "abc"
,因为 s
的前两个字母和接下来的两个字母都等于 "ab"
。
返回删除 s
所需的最大操作数。
提示:
-
1 <= s.length <= 4000
-
s
仅由小写英文字母组成
示例
思路
动态规划+字符串哈希
自己重做虚拟竞赛时,第四题还是没做出来。
记录一下自己的思路:首先每次只能删除一个前缀。前缀能删到什么地方,取决于是否有两段重复的字符串。于是联想到kmp的next数组,尝试后发现并不适用。next数组求解出来的是最长公共前后缀,而不是紧挨着的两段重复字符串。看了眼数据范围,并结合这道题目,认为应该用动态规划来做,但是关于动态规划的状态表示,没有想明白。一开始想的是一维f[i]
,表示删掉前i
个字符的最大次数。但是关于状态转移没想明白。这是因为我状态定义有问题。试想,如果求f[n]
,则考虑其如何转移,肯定是先删掉某个前缀[1, j]
,这记1次操作次数,然后再删除[j + 1, n]
,而后面这段区间,无法再用某个f
来表示,这就是无法划分为子问题。
于是我又想到用二维,f[i, j]
表示删除区间[i, j]
所需要的最大次数。这又想偏了。
因为删除的过程中,始终只能删除某个前缀。而我们最终要求f[1, n]
,而f[1, n]
一定是由删除某个前缀j
转移过来的,删除前缀j
记1次操作次数,则f[1, n] = 1 + f[j + 1, n]
。可以看到f
数组的第二维其实是一直不会变的,一直是n
,所以用二维动规貌似没必要。
想到这里,其实已经比较接近正确思路了。但我卡着一直没想通。
其实,只要设f[i]
表示,删除[i, n]
的最大操作次数即可。
考虑其如何转移,f[i]
要么是直接删除整个字符串,即此时f[i] = 1
;要么是先删掉一部分前缀,假设删除的前缀为[i, j]
,再删[j + 1, n]
,删除前缀[i, j]
记1次,则f[i] = 1 + f[j + 1]
。
那么我们只要枚举一下所有可能的前缀,即可完成状态转移。那么如何快速判断一个前缀能否满足条件呢?这里要用到字符串哈希。
比如我们求解f[i]
时,枚举所有可能的前缀,假设前缀长度为j
则我们需要判断一下区间[i, i + j - 1]
与[i + j, i + j * 2 - 1]
是否相等,这一步可以用字符串哈希将时间复杂度将为。
而我们f[i]
转移时依赖f[i + j]
,所以状态要从右往左计算。如此,能够在时间复杂度
C++:
Java:
总结
T1,T2都是直接暴力模拟;T3是位运算+贪心;T4是动态规划+字符串哈希。
今天重做,只做出3道,还需再接再厉。