
5637. 判断字符串的两半是否相似
题目
给你一个偶数长度的字符串 s 。将其拆分成长度相同的两半,前一半为 a ,后一半为 b 。
两个字符串 相似 的前提是它们都含有相同数目的元音('a','e','i','o','u','A','E','I','O','U')。注意,s 可能同时含有大写和小写字母。
如果 a 和 b 相似,返回 true ;否则,返回 false 。
示例 1:
输入:s = "book"
输出:true
解释:a = "bo" 且 b = "ok" 。a 中有 1 个元音,b 也有 1 个元音。所以,a 和 b 相似。
示例 2:
输入:s = "textbook"
输出:false
解释:a = "text" 且 b = "book" 。a 中有 1 个元音,b 中有 2 个元音。因此,a 和 b 不相似。
注意,元音 o 在 b 中出现两次,记为 2 个。
示例 3:
输入:s = "MerryChristmas"
输出:false
示例 4:
输入:s = "AbCdEfGh"
输出:true
提示:
2 <= s.length <= 1000s.length是偶数s由 大写和小写 字母组成
代码
第一题一般都是水题
class Solution {
public boolean halvesAreAlike(String s) {
int snum = 0;
int tnum = 0;
int len = s.length();
Set<Character> set = new HashSet<>(){{
add('a');
add('e');
add('i');
add('o');
add('u');
add('A');
add('E');
add('I');
add('O');
add('U');
}};
String ss = s.substring(0, len / 2);
String st = s.substring(len / 2, len);
for (int i = 0; i <= len / 2 - 1; i++) {
if (set.contains(ss.charAt(i))) snum++;
if (set.contains(st.charAt(i))) tnum++;
}
return snum == tnum;
}
}
5638. 吃苹果的最大数目
题目
有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,树上会长出 apples[i] 个苹果,这些苹果将会在 days[i] 天后(也就是说,第 i + days[i] 天时)腐烂,变得无法食用。也可能有那么几天,树上不会长出新的苹果,此时用 apples[i] == 0 且 days[i] == 0 表示。
你打算每天 最多 吃一个苹果来保证营养均衡。注意,你可以在这 n 天之后继续吃苹果。
给你两个长度为 n 的整数数组 days 和 apples ,返回你可以吃掉的苹果的最大数目*。*
示例 1:
输入:apples = [1,2,3,5,2], days = [3,2,1,4,2]
输出:7
解释:你可以吃掉 7 个苹果:
- 第一天,你吃掉第一天长出来的苹果。
- 第二天,你吃掉一个第二天长出来的苹果。
- 第三天,你吃掉一个第二天长出来的苹果。过了这一天,第三天长出来的苹果就已经腐烂了。
- 第四天到第七天,你吃的都是第四天长出来的苹果。
示例 2:
输入:apples = [3,0,0,0,0,2], days = [3,0,0,0,0,2]
输出:5
解释:你可以吃掉 5 个苹果:
- 第一天到第三天,你吃的都是第一天长出来的苹果。
- 第四天和第五天不吃苹果。
- 第六天和第七天,你吃的都是第六天长出来的苹果。
提示:
apples.length == ndays.length == n1 <= n <= 2 * 1040 <= apples[i], days[i] <= 2 * 104- 只有在
apples[i] = 0时,days[i] = 0才成立
代码
虽然我比赛时用的方法能过测试用例,但是明显是错误的,因为如果[3,7]区间只有一个苹果,这个时候就不满足条件。这里我默认为区间里的苹果是够的
输入 [1,2,3,1,2] [3,2,1,4,2] 输出 7 预期结果 6
class Solution {
public int eatenApples(int[] apples, int[] days) {
int[][] intervals = new int[apples.length][2];
if (apples.length == 1) {
return apples[0] >= days[0] ? days[0] : apples[0];
}
for (int i = 0; i < apples.length; i++) {
for (int j = 0; j < 2; j++) {
if (j == 0) intervals[i][j] = i;
if (j == 1) intervals[i][j] = i + days[i];
}
}
int[][] res = merge(intervals);
int ret = 0;
for (int i = 0; i < res.length; i++) {
ret = ret + (res[i][1] - res[i][0]);
}
return ret;
}
public int[][] merge(int[][] intervals) {
if (intervals.length == 0) {
return new int[0][2];
}
Arrays.sort(intervals, new Comparator<int[]>() {
public int compare(int[] interval1, int[] interval2) {
return interval1[0] - interval2[0];
}
});
List<int[]> merged = new ArrayList<int[]>();
for (int i = 0; i < intervals.length; ++i) {
int L = intervals[i][0], R = intervals[i][1];
if (merged.size() == 0 || merged.get(merged.size() - 1)[1] < L) {
merged.add(new int[]{L, R});
} else {
merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], R);
}
}
return merged.toArray(new int[merged.size()][]);
}
}
正确思路:优先队列,优先吃最早过期的
class Solution {
public int eatenApples(int[] apples, int[] days) {
// 优先队列 队首为最快过期的 int[0] apple number int[1] 过期时间 按过期时间升序
PriorityQueue<int[]> priorityQueue = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
int ret = 0;
// 遍历apples数组和最后储存的apple
for (int i = 0; i < apples.length || priorityQueue.size() > 0; i++) {
// 1. 移除队首过期的苹果
while(!priorityQueue.isEmpty()) {
int[] temp = priorityQueue.peek();
if (temp[1] <= i) priorityQueue.poll();
else break;
}
// 2. 添加新的苹果 i < apples.length 用来限制当遍历完之后 还有储存的水果
if (i < apples.length && apples[i] > 0) {
priorityQueue.add(new int[]{apples[i], days[i] + i});
}
// 3. 优先吃掉最快过期的
int[] eat = priorityQueue.peek();
if (eat != null && eat[0] > 0) {
ret++;
eat[0] -= 1;
if (eat[0] == 0) priorityQueue.poll();
}
}
return ret;
}
}
class Solution {
public int eatenApples(int[] apples, int[] days) {
//数组的第一位是过期日期。按这个排序
PriorityQueue<int[]> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(o -> o[0]));
int length = apples.length;
int eat = 0;
for (int i = 0; i < length; i++) {
int day = i + days[i];//苹果过期日期
priorityQueue.add(new int[]{day, apples[i]});
while (!priorityQueue.isEmpty()) {
int[] poll = priorityQueue.poll();
if (poll[0] > i) {//苹果是否过期
eat++;//吃苹果
poll[1]--;
if (poll[0] != i + 1 && poll[1] > 0) {//苹果没了
priorityQueue.add(poll);
}
break;
}
}
}
//还有苹果
int day = length;
while (!priorityQueue.isEmpty()) {
int[] poll = priorityQueue.poll();
if (poll[0] > day) {
eat++;
poll[1]--;
if (poll[0] != day + 1 && poll[1] > 0) {//苹果没了
priorityQueue.add(poll);
}
day++;
}
}
return eat;
}
}
5210. 球会落何处
题目
用一个大小为 m x n 的二维网格 grid 表示一个箱子。你有 n 颗球。箱子的顶部和底部都是开着的。
箱子中的每个单元格都有一个对角线挡板,跨过单元格的两个角,可以将球导向左侧或者右侧。
- 将球导向右侧的挡板跨过左上角和右下角,在网格中用
1表示。 - 将球导向左侧的挡板跨过右上角和左下角,在网格中用
-1表示。
在箱子每一列的顶端各放一颗球。每颗球都可能卡在箱子里或从底部掉出来。如果球恰好卡在两块挡板之间的 "V" 形图案,或者被一块挡导向到箱子的任意一侧边上,就会卡住。
返回一个大小为 n 的数组 answer ,其中 answer[i] 是球放在顶部的第 i 列后从底部掉出来的那一列对应的下标,如果球卡在盒子里,则返回 -1 。
示例 1:
输入:grid = [[1,1,1,-1,-1],[1,1,1,-1,-1],[-1,-1,-1,1,1],[1,1,1,1,-1],[-1,-1,-1,-1,-1]]
输出:[1,-1,-1,-1,-1]
解释:示例如图:
b0 球开始放在第 0 列上,最终从箱子底部第 1 列掉出。
b1 球开始放在第 1 列上,会卡在第 2、3 列和第 1 行之间的 "V" 形里。
b2 球开始放在第 2 列上,会卡在第 2、3 列和第 0 行之间的 "V" 形里。
b3 球开始放在第 3 列上,会卡在第 2、3 列和第 0 行之间的 "V" 形里。
b4 球开始放在第 4 列上,会卡在第 2、3 列和第 1 行之间的 "V" 形里。
示例 2:
输入:grid = [[-1]]
输出:[-1]
解释:球被卡在箱子左侧边上。
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 100grid[i][j]为1或-1
代码
卡着时间过了这道题,但是代码写太多,思路也不是很简便,直接用模拟来写的
class Solution {
public int[] findBall(int[][] grid) {
int[] ret = new int[grid[0].length];
if (grid[0].length == 0) return new int[]{-1};
if (grid[0].length == 1) return new int[]{-1};
for (int i = 0; i < grid[0].length; i++) {
ret[i] = i;
}
for (int row = 0; row < grid.length; row++) {
// 如果相加为0 并且-1在右边 证明需要置-1;
for (int col = 1; col < grid[0].length; col++) {
if ((grid[row][col] + grid[row][col - 1]) == 0 && grid[row][col] == -1) {
if (retIndex(col, ret) != -1) ret[retIndex(col, ret)]= -1;
if (retIndex(col - 1, ret) != -1) ret[retIndex(col - 1, ret)] = -1;
}
}
int[] temp = new int[ret.length];
for (int i = 0; i < ret.length; i++) {
temp[i] = ret[i];
}
// 移动每个球的位置
for (int col = 0; col < grid[0].length; col++) {
if(grid[row][col] == 1) {
if (retIndex(col, temp) != -1 && ret[retIndex(col, temp)] != -1) {
ret[retIndex(col, temp)]++;
if (ret[retIndex(col, temp)] == grid[0].length) ret[retIndex(col, temp)] = -1;
}
} else {
if (retIndex(col, temp) != -1 && ret[retIndex(col, temp)] != -1) {
ret[retIndex(col, temp)]--;
}
}
}
}
return ret;
}
public int retIndex(int n, int[] num) {
for (int i = 0; i < num.length; i++) {
if (num[i] == n) return i;
}
return -1;
}
}
正确的思路:递归(DFS)、动态规划(BFS)
class Solution {
public int[] findBall(int[][] grid) {
int row = grid.length;
int col = grid[0].length;
int[] ans = new int[col];
for (int i = 0; i < col; i++) {
ans[i] = out(grid, row, col, i, 0);
}
return ans;
}
private int out(int[][] grid, int row, int col, int x, int y) {
//到达底部
if (y == row) {
return x;
}
//卡在边缘
if (x == 0 && grid[y][x] == -1) {
return -1;
}
if (x == col - 1 && grid[y][x] == 1) {
return -1;
}
//卡在中间
if (grid[y][x] == 1 && grid[y][x + 1] == -1) {
return -1;
}
if (grid[y][x] == -1 && grid[y][x - 1] == 1) {
return -1;
}
//到达下一层
return out(grid, row, col, x + grid[y][x], y + 1);
}
}
class Solution {
public int[] findBall(int[][] grid) {
int row = grid.length;
int col = grid[0].length;
int[] ans = new int[col];
// 默认位置
for (int i = 0; i < col; i++) {
ans[i] = i;
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (ans[j] == -1) {//忽略卡住的球
continue;
}
if (grid[i][ans[j]] == 1 && ans[j] + 1 < col && grid[i][ans[j] + 1] == 1) {
//右移
ans[j]++;
} else if (grid[i][ans[j]] == -1 && ans[j] - 1 >= 0 && grid[i][ans[j] - 1] == -1) {
//左移
ans[j]--;
} else {
//卡住
ans[j] = -1;
}
}
}
return ans;
}
}
5640. 与数组中元素的最大异或值
题目
给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries ,其中 queries[i] = [xi, mi] 。
第 i 个查询的答案是 xi 和任何 nums 数组中不超过 mi 的元素按位异或(XOR)得到的最大值。换句话说,答案是 max(nums[j] XOR xi) ,其中所有 j 均满足 nums[j] <= mi 。如果 nums 中的所有元素都大于 mi,最终答案就是 -1 。
返回一个整数数组 answer 作为查询的答案,其中 answer.length == queries.length 且 answer[i] 是第 i 个查询的答案。
示例 1:
输入:nums = [0,1,2,3,4], queries = [[3,1],[1,3],[5,6]]
输出:[3,3,7]
解释:
1) 0 和 1 是仅有的两个不超过 1 的整数。0 XOR 3 = 3 而 1 XOR 3 = 2 。二者中的更大值是 3 。
2) 1 XOR 2 = 3.
3) 5 XOR 2 = 7.
示例 2:
输入:nums = [5,2,4,6,6,3], queries = [[12,4],[8,1],[6,3]]
输出:[15,-1,5]
提示:
1 <= nums.length, queries.length <= 105queries[i].length == 20 <= nums[j], xi, mi <= 109
代码
没做出来,看评论说这道题也需要用之前的离线思想
Trie字典树+离线思想
nums从小到大排序, queries 从小到大按照mi排序 遍历queries,在nums[i]中所有<=m的数字,可以加入当前字典树中(insert函数) 然后用trie树来查找当前最大异或的数字(query函数),trie树存储的是二进制数字
class Solution {
int[][] son;
int idx;
public int[] maximizeXor(int[] nums, int[][] queries) {
Arrays.sort(nums);
int n = nums.length;
son = new int[n * 31][2];
// 添加index
Pair[] pair = new Pair[queries.length];
for(int i = 0; i < queries.length; i++) {
pair[i] = new Pair(queries[i][0], queries[i][1], i);
}
//把queries按照mi从小到大排序
Arrays.sort(pair, (o1, o2) ->(o1.m - o2.m));
int[] ans = new int[queries.length];
int pos = 0;
for(Pair q : pair) {
//所有<=m的数字,可以加入当前字典树中
while(pos < n && nums[pos] <= q.m) {
insert(nums[pos]);
pos++;
}
//写答案
if(idx == 0) {
ans[q.index] = -1;
}else {
int t = query(q.x);
ans[q.index] = q.x ^ t;
}
}
return ans;
}
//返回此时trie树中的数字里,与a异或最大的数字
private int query(int a){
int p = 0, res = 0;
for(int i = 30; i >= 0; i--){
int u = a >> i & 1;//最高位,次高位,...
if(son[p][u ^ 1] != 0){//如果可以走,u是1,就往0走,u是0,就往1走
res = res * 2 + u ^ 1;
p = son[p][u ^ 1];
}else{
res = res *2 + u;
p = son[p][u];
}
}
return res;
}
//在trie树中插入数字a
private void insert(int a){
int p = 0;
for(int i = 30; i >= 0; i--){
int u = a >> i & 1;
if(son[p][u] == 0){
son[p][u] = ++idx;
}
p = son[p][u];
}
}
}
class Pair{
int x;
int m;
int index;
public Pair(int x, int m, int index) {
this.x = x;
this.m = m;
this.index = index;
}
}