算法思路
1838. 最高频元素的频数思路:题目是只能往上加,不能减,那么就可以先排序,然后从第一个开始像右边扩展窗口,从下标1开始计算,把下标0变成下标1需要花费多少的sum相加然后逐步扩展,一直到sum>大于了题目给出的k然后收缩左窗口因为目前都是全部都是一样的只需要减去当前右窗口减去左窗口的值就是,这一次移除的量然后一直找窗口直到找到最大的窗口。
class Solution {
public int maxFrequency(int[] nums, int k) {
Arrays.sort(nums);
int ans = 1;
int right = 1;
int left = 0;
long sum = 0;
while (right < nums.length) {
sum += (long) (nums[right] - nums[right - 1]) * (right - left);
if (sum > k) {
sum -= nums[right]-nums[left];
left++;
}
ans = Math.max(ans, right - left+1);
right++;
}
return ans;
}
}
713. 乘积小于 K 的子数组思路:是要子数组,必须是连续的所以不能重排序!!!!!!一开始以为可以就写了导致后面错了排查出来了!!!思路就是滑动窗口通用思路,建窗口,如果这个窗口的乘积总和不大于给定的k,那么这个窗口的更小值也可以不大于k就可以给ans加上右窗口减左窗口加一。
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int ans = 0, left = 0;
long sum = 1;
if (k <= 1) return 0;
for (int right = 0; right < nums.length; right++) {
sum = sum * nums[right];
while (sum >= k) {
if (left>=nums.length){
break;
}
sum = sum / nums[left];
left++;
}
ans += right - left + 1;
}
return ans;
}
}
3258. 统计满足 K 约束的子字符串数量 I思路:简单思路,找窗口然后越小越好
class Solution {
public int countKConstraintSubstrings(String s, int k) {
int ans = 0;
int right = 0,left = 0;
HashMap<Character, Integer> charCount = new HashMap<>();
charCount.put('0',0);
charCount.put('1',0);
for (right = 0; right < s.length(); right++) {
char c = s.charAt(right);
charCount.put(c, charCount.getOrDefault(c, 0) + 1);
while (charCount.get('0')>k&&charCount.get('1')>k) {
char leftChar = s.charAt(left);
charCount.put(leftChar, charCount.getOrDefault(leftChar, 0) -1);
left++;
}
ans += right - left + 1;
}
return ans;
}
}
930. 和相同的二元子数组思路:两种方法,第一种是滑动窗口,因为要恰好等于goal,那么就可以求两个子数组一个是最多有goal个1的子数组,然后另一个是最多有goal-1个的子数组然后两个相减就是刚好有goal个1的子数组了。
class Solution {
public int numSubarraysWithSum(int[] nums, int goal) {
return atMost(nums, goal) - atMost(nums, goal - 1);
}
private int atMost(int[] nums, int goal) {
if (goal < 0) return 0;
int left = 0, sum = 0, ans = 0;
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum > goal) {
sum -= nums[left];
left++;
}
ans += right - left + 1;
}
return ans;
}
}
方法二:使用前缀和计算,计算到所有出现过的总和的次数记录在哈希表里面,然后获取当前总和减去goal的数,因为不会有负的情况所以减为负的都是0,然后如果刚好相等就是这个就满足,如果大于0那么前面出现过几次的这个前缀和只要减去这个就是一个满足条件的子数组。
class Solution {
public int numSubarraysWithSum(int[] nums, int goal) {
Map<Integer, Integer> prefixSumCount = new HashMap<>();
prefixSumCount.put(0, 1); // 初始前缀和为 0 出现 1 次
int sum = 0, ans = 0;
for (int num : nums) {
sum += num;
ans += prefixSumCount.getOrDefault(sum - goal, 0);
prefixSumCount.put(sum, prefixSumCount.getOrDefault(sum, 0) + 1);
}
return ans;
}
}
1248. 统计「优美子数组」思路:和上面的方法一一样,使用窗口分别计算两个子数组相减即可。
class Solution {
public int numberOfSubarrays(int[] nums, int k) {
return atQishu(nums, k)-atQishu(nums, k-1);
}
private int atQishu(int[] nums, int k) {
int left = 0, count = 0, ans = 0;
for (int right = 0; right < nums.length; right++) {
if (nums[right] % 2 == 1) {
count++;
}
while (count > k) {
if (nums[left] % 2 == 1) {
count--;
}
left++;
}
ans += right - left + 1;
}
return ans;
}
}
方法二:前缀和,有个小tip**num & 1 和 num % 2 等价**但是使用num & 1 的效率更高一点,前缀和最重要的就是初始化值不要漏加。
class Solution {
public int numberOfSubarrays(int[] nums, int k) {
Map<Integer, Integer> prefixSumCount = new HashMap<>();
prefixSumCount.put(0, 1);
int sum = 0, ans = 0;
for (int num : nums) {
sum += num & 1;
ans += prefixSumCount.getOrDefault(sum - k, 0);
prefixSumCount.put(sum, prefixSumCount.getOrDefault(sum, 0) + 1);
}
return ans;
}
}