时间:2022-03-19 22:30-24:00
地址:竞赛 - 力扣 (LeetCode)
结局:本次依然是做了三道。前三题应该说没有太难,不过有一些边界条件确实有点怪,让我错了好几次。第四题虽然知道要使用动态规划,但瞪大眼珠子想了20多分钟,也没一个靠谱的思路,也就果断下播了。
给你一个整数数组 nums ,它包含 2 * n 个整数。
你需要将 nums 划分成 n 个数对,满足:
- 每个元素只属于一个数对。
- 同一数对中的元素相等 。
如果可以将 nums 划分成 n 个数对,请你返回 true ,否则返回 false 。
示例 1:
输入:nums = [3,2,3,2,2,2]输出:true
解释:
nums 中总共有 6 个元素,所以它们应该被划分成 6 / 2 = 3 个数对。
nums 可以划分成 (2, 2) ,(3, 3) 和 (2, 2) ,满足所有要求。
示例 2:
输入:nums = [1,2,3,4]输出:false
解释:
无法将 nums 划分成 4 / 2 = 2 个数对且满足所有要求。提示:
nums.length == 2 * n1 <= n <= 500
1 <= nums[i] <= 500
思路:这题不难理解哈,排序之后判断前后两个数字是否相等就好。因为数组长度一定是2的倍数,数字取值范围也不大,因此可以简单处理。这个复杂度是 O(nlogn)。
public boolean divideArray(int[] nums) {
int len = nums.length;
if(len%2!=0){
return false;
}
Arrays.sort(nums);
for(int i=0;i
后续整理:快速写完之后,还是花了点时间去想,能不能O(n)搞定。其实也不复杂,只需要确认每个数出现的次数是偶数就行了。弄一个hashmap,完全可以在一次遍历内就搞定。
public boolean divideArray(int[] nums) {
HashMap map = new HashMap<>();
int odd = 0;
for (int num : nums) {
int res = map.getOrDefault(num, 0) + 1;
map.put(num, res);
if (res % 2 == 0) {
odd--;
} else {
odd++;
}
}
return odd == 0;
}
给你一个下标从 0 开始的字符串 text 和另一个下标从 0 开始且长度为 2 的字符串 pattern ,两者都只包含小写英文字母。
你可以在 text 中任意位置插入 一个 字符,这个插入的字符必须是 pattern[0] 或者 pattern[1] 。注意,这个字符可以插入在 text 开头或者结尾的位置。
请你返回插入一个字符后,text 中最多包含多少个等于 pattern 的 子序列 。
子序列 指的是将一个字符串删除若干个字符后(也可以不删除),剩余字符保持原本顺序得到的字符串。
示例 1:
输入:text = "abdcdbc", pattern = "ac"
输出:4
解释:
如果我们在 text[1] 和 text[2] 之间添加 pattern[0] = 'a' ,那么我们得到 "abadcdbc" 。那么 "ac" 作为子序列出现 4 次。
其他得到 4 个 "ac" 子序列的方案还有 "aabdcdbc" 和 "abdacdbc" 。
但是,"abdcadbc" ,"abdccdbc" 和 "abdcdbcc" 这些字符串虽然是可行的插入方案,但是只出现了 3 次 "ac" 子序列,所以不是最优解。
可以证明插入一个字符后,无法得到超过 4 个 "ac" 子序列。
示例 2:输入:text = "aabb", pattern = "ab"
输出:6
解释:
可以得到 6 个 "ab" 子序列的部分方案为 "aaabb" ,"aaabb" 和 "aabbb" 。
提示:
1 <= text.length <=
pattern.length == 2
text 和 pattern 都只包含小写英文字母。
思路:这题当时思路还是来的很快的。以pattern为ab为例,最大的数量一定是把a放在text最左边,或者把b放在text最右边。当时我把计算拆为两步,(1)计算a\b出现次数ca\cb,以及原序列中ab子序列的次数--这个可以通过遇到b时,左边有多少a来进行一个叠加;(2)原次数+max(ca,cb)。虽然想法不错,写的也不慢,不过中间错了两次,是在遗憾。
public long maximumSubsequenceCount(String text, String pattern) {
//错误二,不用long,统计数据会越界,毕竟长度已经有了10^5
long res = 0;
char p1 = pattern.charAt(0);
char p2 = pattern.charAt(1);
long c1 = 0;
long c2 = 0;
for(char c:text.toCharArray()){
if(c==p1){
c1++;
}
else if(c==p2){
c2++;
res+=c1;
}
}
//错误一,没有考虑pattern中两个字符一致的情况
if(p1==p2){
res = c1 + (c1-1)*c1/2;
return res;
}else{
res+=Math.max(c1,c2);
return res;
}
}
后续整理:思路倒是没啥,看看别人的代码是多么简洁。
//https://leetcode-cn.com/u/arignote/
public long maximumSubsequenceCount(String text, String pattern) {
long count = 0, count1 = 0, count2 = 0;
for (char c : text.toCharArray()) {
count += c == pattern.charAt(1) ? count1 : 0;
count2 += c == pattern.charAt(1) ? 1 : 0;
count1 += c == pattern.charAt(0) ? 1 : 0;
}
return count + Math.max(count1, count2);
}
给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)
请你返回将 nums 数组和 至少 减少一半的 最少 操作数。
示例 1:
输入:nums = [5,19,8,1]
输出:3
解释:初始 nums 的和为 5 + 19 + 8 + 1 = 33 。
以下是将数组和减少至少一半的一种方法:
选择数字 19 并减小为 9.5 。
选择数字 9.5 并减小为 4.75 。
选择数字 8 并减小为 4 。
最终数组为 [5, 4.75, 4, 1] ,和为 5 + 4.75 + 4 + 1 = 14.75 。
nums 的和减小了 33 - 14.75 = 18.25 ,减小的部分超过了初始数组和的一半,18.25 >= 33/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。示例 2:
输入:nums = [3,8,20]
输出:3
解释:初始 nums 的和为 3 + 8 + 20 = 31 。
以下是将数组和减少至少一半的一种方法:
选择数字 20 并减小为 10 。
选择数字 10 并减小为 5 。
选择数字 3 并减小为 1.5 。
最终数组为 [1.5, 8, 5] ,和为 1.5 + 8 + 5 = 14.5 。
nums 的和减小了 31 - 14.5 = 16.5 ,减小的部分超过了初始数组和的一半, 16.5 >= 31/2 = 16.5 。
我们需要 3 个操作实现题目要求,所以返回 3 。
可以证明,无法通过少于 3 个操作使数组和减少至少一半。提示:
1 <= nums.length <=
1 <= nums[i] <=
思路:这个的思路也不复杂,每次都缩小最大的数,再去看看有没有减少到一半,把次数记下来就行了。那每次缩小后,当前的最大值怎么找呢?为了图快,我使用了反复排序大法来拿最大值,成功超时。后来还是老实使用了PriorityQueue来做(实力不足,就没有自己实现堆了)。
public int halveArray(int[] nums) {
int count = 0;
double sum = 0;
int len = nums.length;
PriorityQueue priorityQueue = new PriorityQueue<>(new Comparator() {
@Override
public int compare(Double o1, Double o2) {
return o2-o1>0?1:-1;
}
});
for(int i=0;i
后续整理:主流思路。没有什么特别值得说的。
给你一个下标从 0 开始的 二进制 字符串 floor ,它表示地板上砖块的颜色。
floor[i] = '0' 表示地板上第 i 块砖块的颜色是 黑色 。
floor[i] = '1' 表示地板上第 i 块砖块的颜色是 白色 。
同时给你 numCarpets 和 carpetLen 。你有 numCarpets 条 黑色 的地毯,每一条 黑色 的地毯长度都为 carpetLen 块砖块。请你使用这些地毯去覆盖砖块,使得未被覆盖的剩余 白色 砖块的数目 最小 。地毯相互之间可以覆盖。请你返回没被覆盖的白色砖块的 最少 数目。
示例 1:
示例 2:提示:
1 <= carpetLen <= floor.length <= 1000
floor[i] 要么是 '0' ,要么是 '1' 。
1 <= numCarpets <= 1000
思路:这题直观上能感觉到是一个动态规划的题目,但转移关系是着实摸不着头脑。挣扎了二十多分钟后放弃了。
后续整理:
dp[i][j] 表示考虑前面长度为 j 的地板中,用了 i 条地毯,最后剩余最少白色砖块。
状态转移方程:
- 当 i == 0 时,此时不用地毯, dp[0][j] 表示前 j 个砖块中白色砖块数目。
- 当 i != 0 时,dp[i][j] = min(dp[i][j - 1] + count[j], dp[i - 1][j - carpetLen]);
其中 count[j] 表示第 j 块地板是否为白色砖块,
- dp[i][j-1] + count[j] 表示前 j - 1 块地板用了i 条地毯,剩余最少的白色砖块,加上第 j 块地板是否为白色砖块。(即第 j 个位置不用地毯)
- dp[i - 1][j - carpetLen] 表示在下标 j 这里用了地毯,那么 dp[i][j] 就和前 (j - carpetLen) 块地板中用了 i - 1 条地毯剩余的白色砖块数目一致。(即第 j 个位置用了地毯)
链接:https://leetcode-cn.com/problems/minimum-white-tiles-after-covering-with-carpets/solution/tan-xin-by-cyber-space-iwvd/
class Solution {
public int minimumWhiteTiles(String floor, int n, int carpetLen) {
var m = floor.length();
var f = new int[n + 1][m];
f[0][0] = floor.charAt(0) % 2;
for (var i = 1; i < m; ++i)
f[0][i] = f[0][i - 1] + floor.charAt(i) % 2;
for (var i = 1; i <= n; ++i)
// j < carpetLen 的 f[i][j] 均为 0
for (var j = carpetLen; j < m; ++j)
f[i][j] = Math.min(f[i][j - 1] + floor.charAt(j) % 2, f[i - 1][j - carpetLen]);
return f[n][m - 1];
}
}
//链接:https://leetcode-cn.com/problems/minimum-white-tiles-after-covering-with-carpets/solution/by-endlesscheng-pa3v/