目录
一,3168. 候诊室中的最少椅子数
二,3169. 无需开会的工作日
三,3170. 删除星号以后字典序最小的字符串
四,3171. 找到按位与最接近 K 的子数组
一,3168. 候诊室中的最少椅子数
本题是一道模拟题,直接使用一个变量存储实时的人数,每次修改之后,与之前的最大值比较,得出答案。
代码如下:
class Solution {public int minimumChairs(String s) {int cnt = 0, ans = 0;for(char ch : s.toCharArray()){if(ch == 'E'){cnt++;}else{cnt--;}ans = Math.max(ans, cnt);}return ans;}
}
二,3169. 无需开会的工作日
本题要求没有安排会议的天数,可以正难则反,使用总天数减去工作的天数;也可以直接累加休息的天数。两者意思相同,能理解那种就使用那种。
代码如下:
//正难则反 -> 使用总天数 - 工作天数
class Solution {public int countDays(int days, int[][] meetings) {Arrays.sort(meetings,(x,y)->x[0]-y[0]);int s = 1, e = 0;for(int[] x : meetings){if(x[0] > e){days -= e - s + 1;s = x[0];}e = Math.max(e, x[1]);}days -= e - s + 1;return days;}
}//直接算 -> 累加休息的天数
class Solution {public int countDays(int days, int[][] meetings) {Arrays.sort(meetings,(x,y)->x[0]-y[0]);int ans = 0;int pre = 0;//前一段工作日的最后一天for(int[] x : meetings){ans += Math.max(0, x[0] - pre - 1);pre = Math.max(pre, x[1]);}return ans+days-pre;}
}
三,3170. 删除星号以后字典序最小的字符串
本题题意——每次遇到一个' * '字符,就要删去它本身及其前面字典序最小的字符,如果有多个,可以删除其中任意一个,求删除所有 ' * ' 字符后,剩余字符串的最小字符串。
在写本题之前,需要弄清楚一个问题,如果' * '前面有多个相同的最小字符,究竟要删除哪一个,才能使得剩余的字符串最小?例如:"aaba*",我们只有删除最后一个'a'才能使剩余字符串最小。
弄清上述问题后,就可以写代码了,我们可以使用一个堆来统计遇到 '*' 字符前,删除字符的优先级(如果字符不同,按字典序排序;如果字符相同,下标大的排在前面),再使用一个哈希表来统计删除的字符下标,最后再遍历一次字符串s,将保留下来的字符串起来,得到答案。
代码如下:
class Solution {public String clearStars(String s) {char[] ch = s.toCharArray();PriorityQueue<int[]> que = new PriorityQueue<>((x, y) -> {if(x[0] == y[0]) return y[1] - x[1];//如果字符相同,下标大的排在前面return x[0] - y[0];//如果字符不同,按字典序排序});Set<Integer> set = new HashSet<>();for(int i=0; i<ch.length; i++){if(ch[i] != '*'){que.offer(new int[]{ch[i]-'a', i});}else{set.add(que.poll()[1]);//统计删除的字符set.add(i);}}StringBuilder res = new StringBuilder();for(int i=0; i<ch.length; i++){if(!set.contains(i)){res.append(ch[i]);}}return res.toString();}
}
四,3171. 找到按位与最接近 K 的子数组
1.位运算性质 + 数学
- 该题如果暴力求解,它的时间复杂度为O(n^2),会超时,但是如果添加一个 if 判断,就可以将它的复杂度降为O(nlogU),画个图理解一下:
class Solution {//O(nlogU) U=max(nums)public int minimumDifference(int[] nums, int k) {int ans = Integer.MAX_VALUE;for(int i=0; i<nums.length; i++){int x = nums[i];ans = Math.min(ans, Math.abs(k - x));for(int j=i-1; j>=0; j--){if((nums[j]&x)==nums[j]) break;// nums[j] &= x 只会变小,而最多只有32位不一样// 所以时间复杂度最多就是O(32*n)nums[j] &= x;ans = Math.min(ans, Math.abs(k - nums[j]));}}return ans;}
}
2.滑动窗口
- &的性质——&的数越多,&值越小,可以使用滑窗,假设and是【l,r】的&值,如果 and小于 k(再继续&的话,and会更小,abs(k-and)的值就会变大),所以要将 nums[l] 回退,l++。注意:要不断地更新ans的值。
class Solution {public int minimumDifference(int[] nums, int k) {int ans = Integer.MAX_VALUE;int[] cnt = new int[32];int and = -1;for(int l=0,r=0; r<nums.length; r++){and &= nums[r];for(int i=0; i<32; i++){cnt[i] += (nums[r]>>i)&1;}ans = Math.min(ans, Math.abs(k - and));while(l <= r && and < k){for(int i=0; i<32; i++){cnt[i] -= (nums[l]>>i)&1;if(cnt[i] == r-l){and |= (1<<i);}}l++;ans = Math.min(ans, Math.abs(k - and));}}return ans;}
}