1493.删掉一个元素以后全为1的最长子数组

目标

给你一个二进制数组 nums ,你需要从中删掉一个元素。

请你在删掉元素的结果数组中,返回最长的且只包含 1 的非空子数组的长度。

如果不存在这样的子数组,请返回 0 。

示例 1:

输入:nums = [1,1,0,1]
输出:3
解释:删掉位置 2 的数后,[1,1,1] 包含 3 个 1 。

示例 2:

输入:nums = [0,1,1,1,0,1,1,0,1]
输出:5
解释:删掉位置 4 的数字后,[0,1,1,1,1,1,0,1] 的最长全 1 子数组为 [1,1,1,1,1] 。

示例 3:

输入:nums = [1,1,1]
输出:2
解释:你必须要删除一个元素。

说明:

  • 1 <= nums.length <= 10^5
  • nums[i] 要么是 0 要么是 1 。

思路

有一个二进制数组 nums,从中删除一个元素,求剩余元素中连续的 1 的最大长度。

计算从当前下标为起点的连续 1 的结束下标,end - start 表示连续 1 的长度,允许删掉一个元素可以直接加上 以 end + 1 为起点的连续 1 的个数。

更优的解法是使用滑动窗口计算最长子数组长度,要求窗口内部至多一个 0

代码


/**
 * @date 2025-08-24 12:20
 */
public class LongestSubarray1493 {

    public int longestSubarray(int[] nums) {
        int n = nums.length;
        int res = 0;
        int i = 0;
        while (i < n) {
            int start = i;
            while (i < n && nums[i] == 1) {
                i++;
            }
            for (int j = start; j < i; j++) {
                nums[j] = i;
            }
            if (i == start) {
                nums[i] = i;
                i++;
            }
        }
        for (int j = 0; j < n; j++) {
            res = Math.max(res, nums[j] - j + (nums[j] < n - 1 ? nums[nums[j] + 1] - nums[j] - 1 : 0));
        }
        return res == n ? n - 1 : res;
    }

}

性能

2348.全0子数组的数目

目标

给你一个整数数组 nums ,返回全部为 0 的 子数组 数目。

子数组 是一个数组中一段连续非空元素组成的序列。

示例 1:

输入:nums = [1,3,0,0,2,0,0,4]
输出:6
解释:
子数组 [0] 出现了 4 次。
子数组 [0,0] 出现了 2 次。
不存在长度大于 2 的全 0 子数组,所以我们返回 6 。

示例 2:

输入:nums = [0,0,0,2,0,0]
输出:9
解释:
子数组 [0] 出现了 5 次。
子数组 [0,0] 出现了 3 次。
子数组 [0,0,0] 出现了 1 次。
不存在长度大于 3 的全 0 子数组,所以我们返回 9 。

示例 3:

输入:nums = [2,10,2019]
输出:0
解释:没有全 0 子数组,所以我们返回 0 。

说明:

  • 1 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9

思路

返回数组的全 0 子数组个数。

长度为 k 的子数组个数为 (1 + k) * k / 2。可以使用贪心策略,如果当前元素是 0 找到以它为起点的最长连续 0 数组,计算子数组个数。

代码


/**
 * @date 2025-08-19 8:54
 */
public class ZeroFilledSubarray2348 {

    public long zeroFilledSubarray(int[] nums) {
        long res = 0L;
        int n = nums.length;
        int i = 0;
        while (i < n){
            if (nums[i] != 0){
                i++;
                continue;
            }
            int start = i;
            while (i < n && nums[i] == 0){
                i++;
            }
            int cnt = i - start;
            res += (1L + cnt) * cnt / 2;
        }

        return res;
    }
}

性能

837.新21点

目标

爱丽丝参与一个大致基于纸牌游戏 “21点” 规则的游戏,描述如下:

爱丽丝以 0 分开始,并在她的得分少于 k 分时抽取数字。 抽取时,她从 [1, maxPts] 的范围中随机获得一个整数作为分数进行累计,其中 maxPts 是一个整数。 每次抽取都是独立的,其结果具有相同的概率。

当爱丽丝获得 k 分 或更多分 时,她就停止抽取数字。

爱丽丝的分数不超过 n 的概率是多少?

与实际答案误差不超过 10^-5 的答案将被视为正确答案。

示例 1:

输入:n = 10, k = 1, maxPts = 10
输出:1.00000
解释:爱丽丝得到一张牌,然后停止。

示例 2:

输入:n = 6, k = 1, maxPts = 10
输出:0.60000
解释:爱丽丝得到一张牌,然后停止。 在 10 种可能性中的 6 种情况下,她的得分不超过 6 分。

示例 3:

输入:n = 21, k = 17, maxPts = 10
输出:0.73278

说明:

  • 0 <= k <= n <= 10^4
  • 1 <= maxPts <= 10^4

思路

代码

性能

904.水果成篮

目标

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例 1:

输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。

示例 2:

输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

示例 3:

输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

示例 4:

输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。

说明:

  • 1 <= fruits.length <= 10^5
  • 0 <= fruits[i] < fruits.length

思路

求满足条件的子数组的最大长度,要求子数组最多包含两个不同的元素。

滑动窗口。

代码


/**
 * @date 2025-08-04 8:51
 */
public class TotalFruit904 {

    public int totalFruit(int[] fruits) {
        int n = fruits.length;
        int res = 0;
        Map<Integer, Integer> map = new HashMap<>();
        int l = 0;
        for (int i = 0; i < n; i++) {
            map.merge(fruits[i], 1, Integer::sum);
            while (map.size() > 2) {
                map.merge(fruits[l], -1, Integer::sum);
                if (map.get(fruits[l]) == 0) {
                    map.remove(fruits[l]);
                }
                l++;
            }
            res = Math.max(res, i - l + 1);
        }
        return res;
    }

}

性能

2106.摘水果

目标

在一个无限的 x 坐标轴上,有许多水果分布在其中某些位置。给你一个二维整数数组 fruits ,其中 fruits[i] = [positioni, amounti] 表示共有 amounti 个水果放置在 positioni 上。fruits 已经按 positioni 升序排列 ,每个 positioni 互不相同 。

另给你两个整数 startPos 和 k 。最初,你位于 startPos 。从任何位置,你可以选择 向左或者向右 走。在 x 轴上每移动 一个单位 ,就记作 一步 。你总共可以走 最多 k 步。你每达到一个位置,都会摘掉全部的水果,水果也将从该位置消失(不会再生)。

返回你可以摘到水果的 最大总数 。

示例 1:

输入:fruits = [[2,8],[6,3],[8,6]], startPos = 5, k = 4
输出:9
解释:
最佳路线为:
- 向右移动到位置 6 ,摘到 3 个水果
- 向右移动到位置 8 ,摘到 6 个水果
移动 3 步,共摘到 3 + 6 = 9 个水果

示例 2:

输入:fruits = [[0,9],[4,1],[5,7],[6,2],[7,4],[10,9]], startPos = 5, k = 4
输出:14
解释:
可以移动最多 k = 4 步,所以无法到达位置 0 和位置 10 。
最佳路线为:
- 在初始位置 5 ,摘到 7 个水果
- 向左移动到位置 4 ,摘到 1 个水果
- 向右移动到位置 6 ,摘到 2 个水果
- 向右移动到位置 7 ,摘到 4 个水果
移动 1 + 3 = 4 步,共摘到 7 + 1 + 2 + 4 = 14 个水果

示例 3:

输入:fruits = [[0,3],[6,4],[8,5]], startPos = 3, k = 2
输出:0
解释:
最多可以移动 k = 2 步,无法到达任一有水果的地方

说明:

  • 1 <= fruits.length <= 10^5
  • fruits[i].length == 2
  • 0 <= startPos, positioni <= 2 * 10^5
  • 对于任意 i > 0 ,positioni-1 < positioni 均成立(下标从 0 开始计数)
  • 1 <= amounti <= 10^4
  • 0 <= k <= 2 * 10^5

思路

代码

性能

2411.按位或最大的最小子数组长度

目标

给你一个长度为 n 下标从 0 开始的数组 nums ,数组中所有数字均为非负整数。对于 0 到 n - 1 之间的每一个下标 i ,你需要找出 nums 中一个 最小 非空子数组,它的起始位置为 i (包含这个位置),同时有 最大 的 按位或运算值 。

  • 换言之,令 Bij 表示子数组 nums[i...j] 的按位或运算的结果,你需要找到一个起始位置为 i 的最小子数组,这个子数组的按位或运算的结果等于 max(Bik) ,其中 i <= k <= n - 1 。

一个数组的按位或运算值是这个数组里所有数字按位或运算的结果。

请你返回一个大小为 n 的整数数组 answer,其中 answer[i]是开始位置为 i ,按位或运算结果最大,且 最短 子数组的长度。

子数组 是数组里一段连续非空元素组成的序列。

示例 1:

输入:nums = [1,0,2,1,3]
输出:[3,3,2,2,1]
解释:
任何位置开始,最大按位或运算的结果都是 3 。
- 下标 0 处,能得到结果 3 的最短子数组是 [1,0,2] 。
- 下标 1 处,能得到结果 3 的最短子数组是 [0,2,1] 。
- 下标 2 处,能得到结果 3 的最短子数组是 [2,1] 。
- 下标 3 处,能得到结果 3 的最短子数组是 [1,3] 。
- 下标 4 处,能得到结果 3 的最短子数组是 [3] 。
所以我们返回 [3,3,2,2,1] 。

示例 2:

输入:nums = [1,2]
输出:[2,1]
解释:
下标 0 处,能得到最大按位或运算值的最短子数组长度为 2 。
下标 1 处,能得到最大按位或运算值的最短子数组长度为 1 。
所以我们返回 [2,1] 。

说明:

  • n == nums.length
  • 1 <= n <= 10^5
  • 0 <= nums[i] <= 10^9

思路

有一个数组 nums,求以每个元素为起点的子数组,取得最大或值的最小长度。res[i] 表示以 i 为起点的子数组中,数组元素按位或取得最大值时的最小长度。

枚举右端点,将当前元素与前面的元素进行或运算,并覆盖原来的元素值。对于当前元素 jnums[i] 中存的是 i ~ j 的或值,站在集合的角度来看,nums[i + 1]nums[i] 的子集。在进行或运算时,如果或值没有变大,就没有必要继续向前了。

代码


/**
 * @date 2025-07-29 9:39
 */
public class SmallestSubarrays2411 {

    public int[] smallestSubarrays(int[] nums) {
        int n = nums.length;
        int[] res = new int[n];
        for (int i = 0; i < n; i++) {
            int num = nums[i];
            res[i] = 1;
            for (int j = i - 1; j >= 0 && (num | nums[j]) != nums[j]; j--) {
                nums[j] |= num;
                res[j] = i - j + 1;
            }
        }
        return res;
    }

}

性能

1695.删除子数组的最大得分

目标

给你一个正整数数组 nums ,请你从中删除一个含有 若干不同元素 的子数组。删除子数组的 得分 就是子数组各元素之 和 。

返回 只删除一个 子数组可获得的 最大得分 。

如果数组 b 是数组 a 的一个连续子序列,即如果它等于 a[l],a[l+1],...,a[r] ,那么它就是 a 的一个子数组。

示例 1:

输入:nums = [4,2,4,5,6]
输出:17
解释:最优子数组是 [2,4,5,6]

示例 2:

输入:nums = [5,2,1,2,5,2,1,2,5]
输出:8
解释:最优子数组是 [5,2,1] 或 [1,2,5]

说明:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^4

思路

有一个正整数数组 nums,求一个不含重复元素的子数组,使得子数组的元素和最大。

滑动窗口,要求窗口内部不含重复元素,求窗口内元素和的最大值。

代码


/**
 * @date 2025-07-22 8:50
 */
public class MaximumUniqueSubarray1695 {

    public int maximumUniqueSubarray(int[] nums) {
        int n = nums.length;
        Set<Integer> set = new HashSet<>();
        int l = 0;
        int sum = 0;
        int res = 0;
        for (int r = 0; r < n; r++) {
            sum += nums[r];
            while (l < r && set.contains(nums[r])) {
                sum -= nums[l];
                set.remove(nums[l++]);
            }
            set.add(nums[r]);
            res = Math.max(res, sum);
        }
        return res;
    }

}

性能

3439.重新安排会议得到最多空余时间I

目标

给你一个整数 eventTime 表示一个活动的总时长,这个活动开始于 t = 0 ,结束于 t = eventTime 。

同时给你两个长度为 n 的整数数组 startTime 和 endTime 。它们表示这次活动中 n 个时间 没有重叠 的会议,其中第 i 个会议的时间为 [startTime[i], endTime[i]] 。

你可以重新安排 至多 k 个会议,安排的规则是将会议时间平移,且保持原来的 会议时长 ,你的目的是移动会议后 最大化 相邻两个会议之间的 最长 连续空余时间。

移动前后所有会议之间的 相对 顺序需要保持不变,而且会议时间也需要保持互不重叠。

请你返回重新安排会议以后,可以得到的 最大 空余时间。

注意,会议 不能 安排到整个活动的时间以外。

示例 1:

输入:eventTime = 5, k = 1, startTime = [1,3], endTime = [2,5]
输出:2
解释:
将 [1, 2] 的会议安排到 [2, 3] ,得到空余时间 [0, 2] 。

示例 2:

输入:eventTime = 10, k = 1, startTime = [0,2,9], endTime = [1,4,10]
输出:6
解释:
将 [2, 4] 的会议安排到 [1, 3] ,得到空余时间 [3, 9] 。

示例 3:

输入:eventTime = 5, k = 2, startTime = [0,1,2,3,4], endTime = [1,2,3,4,5]
输出:0
解释:
活动中的所有时间都被会议安排满了。

说明:

  • 1 <= eventTime <= 10^9
  • n == startTime.length == endTime.length
  • 2 <= n <= 10^5
  • 1 <= k <= n
  • 0 <= startTime[i] < endTime[i] <= eventTime
  • endTime[i] <= startTime[i + 1] 其中 i 在范围 [0, n - 2] 之间。

思路

有一个活动有 n 个时间不重叠的会议,重新安排 k 个会议议程,使得空余时间最大。

先根据会议时间排序,滑动窗口计算 k 个会议的总时间,以及 [end[l - 1], start[r + 1]],相减即为窗口内的最大空余时间。

代码


/**
 * @date 2025-07-09 8:52
 */
public class MaxFreeTime3439 {

    public int maxFreeTime(int eventTime, int k, int[] startTime, int[] endTime) {
        int n = startTime.length;
        int[][] interval = new int[n + 2][2];
        interval[0][1] = 0;
        for (int i = 1; i <= n; i++) {
            interval[i] = new int[]{startTime[i - 1], endTime[i - 1]};
        }
        interval[n + 1][0] = eventTime;
        int l = 1;
        int res = 0, sum = 0;
        for (int r = 1; r <= n; r++) {
            sum += interval[r][1] - interval[r][0];
            if (r - l == k - 1) {
                res = Math.max(res, interval[r + 1][0] - interval[l - 1][1] - sum);
                sum -= interval[l][1] - interval[l][0];
                l++;
            }
        }
        return res;
    }

}

性能

2200.找出数组中的所有K近邻下标

目标

给你一个下标从 0 开始的整数数组 nums 和两个整数 key 和 k 。K 近邻下标 是 nums 中的一个下标 i ,并满足至少存在一个下标 j 使得 |i - j| <= k 且 nums[j] == key 。

以列表形式返回按 递增顺序 排序的所有 K 近邻下标。

示例 1:

输入:nums = [3,4,9,1,3,9,5], key = 9, k = 1
输出:[1,2,3,4,5,6]
解释:因此,nums[2] == key 且 nums[5] == key 。
- 对下标 0 ,|0 - 2| > k 且 |0 - 5| > k ,所以不存在 j 使得 |0 - j| <= k 且 nums[j] == key 。所以 0 不是一个 K 近邻下标。
- 对下标 1 ,|1 - 2| <= k 且 nums[2] == key ,所以 1 是一个 K 近邻下标。
- 对下标 2 ,|2 - 2| <= k 且 nums[2] == key ,所以 2 是一个 K 近邻下标。
- 对下标 3 ,|3 - 2| <= k 且 nums[2] == key ,所以 3 是一个 K 近邻下标。
- 对下标 4 ,|4 - 5| <= k 且 nums[5] == key ,所以 4 是一个 K 近邻下标。
- 对下标 5 ,|5 - 5| <= k 且 nums[5] == key ,所以 5 是一个 K 近邻下标。
- 对下标 6 ,|6 - 5| <= k 且 nums[5] == key ,所以 6 是一个 K 近邻下标。
因此,按递增顺序返回 [1,2,3,4,5,6] 。 

示例 2:

输入:nums = [2,2,2,2,2], key = 2, k = 2
输出:[0,1,2,3,4]
解释:对 nums 的所有下标 i ,总存在某个下标 j 使得 |i - j| <= k 且 nums[j] == key ,所以每个下标都是一个 K 近邻下标。 
因此,返回 [0,1,2,3,4] 。

说明:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 1000
  • key 是数组 nums 中的一个整数
  • 1 <= k <= nums.length

思路

返回数组中元素值为 keyk 临近下标,即元素 key 的下标以及其左右 k 个下标。要求以递增顺序返回,不能包含重复下标。

遍历数组,判断 nums[i] 是否等于 k,如果相等则将左右两侧的 k 个下标加入答案。使用一个指针标记当前已经记录的最大下标,避免将重复的下标加入答案。

代码


/**
 * @date 2025-06-24 0:11
 */
public class FindKDistantIndices2200 {

    public List<Integer> findKDistantIndices(int[] nums, int key, int k) {
        List<Integer> res = new ArrayList<>();
        int n = nums.length;
        int r = -1;
        for (int i = 0; i < n; i++) {
            if (nums[i] == key) {
                int l = Math.max(r + 1, i - k);
                r = Math.min(n - 1, i + k);
                for (int j = l; j <= r; j++) {
                    res.add(j);
                }
            }
        }
        return res;
    }

}

性能

3445.奇偶频次间的最大差值II

目标

给你一个字符串 s 和一个整数 k 。请你找出 s 的子字符串 subs 中两个字符的出现频次之间的 最大 差值,freq[a] - freq[b] ,其中:

  • subs 的长度 至少 为 k 。
  • 字符 a 在 subs 中出现奇数次。
  • 字符 b 在 subs 中出现偶数次。

返回 最大 差值。

注意 ,subs 可以包含超过 2 个 互不相同 的字符。.

子字符串 是字符串中的一个连续字符序列。

示例 1:

输入:s = "12233", k = 4
输出:-1
解释:
对于子字符串 "12233" ,'1' 的出现次数是 1 ,'3' 的出现次数是 2 。差值是 1 - 2 = -1 。

示例 2:

输入:s = "1122211", k = 3
输出:1
解释:
对于子字符串 "11222" ,'2' 的出现次数是 3 ,'1' 的出现次数是 2 。差值是 3 - 2 = 1 。

示例 3:

输入:s = "110", k = 3
输出:-1

说明:

  • 3 <= s.length <= 3 * 10^4
  • s 仅由数字 '0' 到 '4' 组成。
  • 输入保证至少存在一个子字符串是由一个出现奇数次的字符和一个出现偶数次的字符组成。
  • 1 <= k <= s.length

思路

有一个字符串 s 仅由 0 ~ 4 组成,求其长度至少为 k 的子串中,3442_奇偶频次间的最大差值I 的最大值。

// todo

代码

性能