前言
只写前三题,第四题实力不够暂时不写
第一题 1897. 重新分配字符使所有字符串都相等(简单)
给你一个字符串数组 words(下标 从 0 开始 计数)。
在一步操作中,需先选出两个 不同 下标 i 和 j,其中 words[i] 是一个非空字符串,接着将 words[i] 中的 任一 字符移动到 words[j] 中的 任一 位置上。
如果执行任意步操作可以使 words 中的每个字符串都相等,返回 true ;否则,返回 false 。
提示:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i] 由小写英文字母组成
当时思路
先分析,只需要得出能否达成目标,不需要取出最小操作步骤之类的,很显然只需要每个字母能均匀的分散到各个words就可以。
再看问题规模,长度在一百以内,每个词长度也在一百以内,遍历一遍操作次数在10000以内,可以直接遍历
直接取出每个字母出现的次数,每个出现次数非0的值看看能否平均分布就可
class Solution {
public boolean makeEqual(String[] words) {
int[] array = new int[26];
for (String word : words) {
for (int i = 0; i < word.length(); i++) {
array[word.charAt(i) - 'a']++;
}
}
int len = words.length;
for (int i = 0; i < array.length; i++) {
if(array[i] % len != 0){
return false;
}
}
return true;
}
}
第二题 1898. 可移除字符的最大数目(中等)
给你两个字符串 s 和 p ,其中 p 是 s 的一个 子序列 。同时,给你一个元素 互不相同 且下标 从 0 开始 计数的整数数组 removable ,该数组是 s 中下标的一个子集(s 的下标也 从 0 开始 计数)。
请你找出一个整数 k(0 <= k <= removable.length),选出 removable 中的 前 k 个下标,然后从 s 中移除这些下标对应的 k 个字符。整数 k 需满足:在执行完上述步骤后, p 仍然是 s 的一个 子序列 。更正式的解释是,对于每个 0 <= i < k ,先标记出位于 s[removable[i]] 的字符,接着移除所有标记过的字符,然后检查 p 是否仍然是 s 的一个子序列。
返回你可以找出的 最大 k ,满足在移除字符后 p 仍然是 s 的一个子序列。
字符串的一个 子序列 是一个由原字符串生成的新字符串,生成过程中可能会移除原字符串中的一些字符(也可能不移除)但不改变剩余字符之间的相对顺序。
提示:
1 <= p.length <= s.length <= 105
0 <= removable.length < s.length
0 <= removable[i] < s.length
p 是 s 的一个 子字符串
s 和 p 都由小写英文字母组成
removable 中的元素 互不相同
当时思路
比赛时先做的第三题,剩下差不多一个小时在这个题目上面,一直没做出来,当时想判断p是否是s的子序列的复杂度为p.length*s.length(这是子串,不是子序列!),这最大到了十亿,只是一次检查就这么多,所以认为此路不通,导致一直没有思路,甚至想到的唯一办法,用dp遍历一次将所有子序列求出来,然后计算每个字母出现次数,再一个个减去removable对应的字母,如果字母出现次数不为0则仍然存在子序列,只是dp的复杂度也没办法降低,更何况空间复杂度也会很高。
正确思路
实际检查子序列的复杂度最大为m+n,所以如果直接暴力,复杂度为removable.length * (p.length + s.length),显然removable这里可以使用二分法,则将复杂度降低为log removable.length * (p.length + s.length)
代码如下,本身可以不用判断reset,只是考虑到取二段后,去标记字符有一段是重复的,例如第一次标记了0-mid间的数,然后取左半段,那么其中只需要清除左半段中的右半部分的标记,减少运算次数,整体仍然是二分的思想,中间使用origin赋值,尽量减少在循环里创建对象,减少内存使用(或者说是内存抖动)
public int maximumRemovals(String s, String p, int[] removable) {
char[] sChars = s.toCharArray();
char[] origin = sChars.clone();
char[] pChars = p.toCharArray();
int left = 0;
int right = removable.length;
boolean reset = false;
while (left < right) {
int mid = ((right - left) >> 1) + left;
if(reset){
for (int i = mid + 1; i <= right; i++) {
sChars[removable[i]] = origin[removable[i]];
}
}else {
for (int i = left; i <= mid; i++) {
sChars[removable[i]] = '#';
}
}
if ((reset = check(sChars, pChars))) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
第三题 1899. 合并若干三元组以形成目标三元组(中等)
三元组 是一个由三个整数组成的数组。给你一个二维整数数组 triplets ,其中 triplets[i] = [ai, bi, ci] 表示第 i 个 三元组 。同时,给你一个整数数组 target = [x, y, z] ,表示你想要得到的 三元组 。
为了得到 target ,你需要对 triplets 执行下面的操作 任意次(可能 零 次):
选出两个下标(下标 从 0 开始 计数)i 和 j(i != j),并 更新 triplets[j] 为 [max(ai, aj), max(bi, bj), max(ci, cj)] 。
例如,triplets[i] = [2, 5, 3] 且 triplets[j] = [1, 7, 5],triplets[j] 将会更新为 [max(2, 1), max(5, 7), max(3, 5)] = [2, 7, 5] 。
如果通过以上操作我们可以使得目标 三元组 target 成为 triplets 的一个 元素 ,则返回 true ;否则,返回 false 。
提示:
1 <= triplets.length <= 105
triplets[i].length == target.length == 3
1 <= ai, bi, ci, x, y, z <= 1000
当时思路
先仔细看题,思路还是比较容易,只需要取出所有三元素小于等于目标三元素的值,全部取最大值即可
以下是当时的解法,时间复杂度上就是o(n) 空间复杂度o(1) 所以从算法上是最优解法,细节仍然可以优化
class Solution {
public boolean mergeTriplets(int[][] triplets, int[] target) {
int[] temp = new int[3];
int len = triplets.length;
for (int i = 0; i < len; i++) {
boolean flag = true;
for (int j = 0; j < 3; j++) {
if (triplets[i][j] > target[j]) {
flag = false;
break;
}
}
if(flag){
for (int j = 0; j < 3; j++) {
temp[j] = Math.max(temp[j], triplets[i][j]);
}
}
}
for (int i = 0; i < 3; i++) {
if (temp[i] != target[i]) {
return false;
}
}
return true;
}
}
优化思路
在flag的判断中可以加上只有当triplets[i]有起码一个到目的值才进入
在判断的里面,可以改为布尔数组,达到值就可以不做判断直接跳过,这样结果的判断也只需要三个值做且运算
以下为LeetCode中的最优算法,细节做了足够的优化
class Solution {
public boolean check(int[][]m,int[]c,int idx){
for(int i=0;i<m.length;i++){
if(m[i][idx]==c[idx]&&m[i][(idx+1)%3]<=c[(idx+1)%3]&&m[i][(idx+2)%3]<=c[(idx+2)%3])
return true;
}
return false;
}
public boolean mergeTriplets(int[][] triplets, int[] target) {
boolean a=false,b=false,c=false;
a=check(triplets,target,0);
b=check(triplets,target,1);
c=check(triplets,target,2);
return a&&b&&c;
}
}
总结
第二题的问题太大了,子串子序列的复杂度就随便想一下,导致后面一个小时挂在这
做的比较好的点是第二题初步计算复杂度认为没思路就看了第三题,迅速完成了第三题。
整体来说本周周赛难度较小,除了第四题,暂时没到第四题的水平