leetcode
leetcode 1951 ~ 2000
含最多 K 个可整除元素的子数组

含最多 K 个可整除元素的子数组

难度:

标签:

题目描述

代码结果

运行时间: 52 ms, 内存: 16.8 MB


/*
 * 思路:
 * 1. 使用 Java Stream 生成所有可能的子数组。
 * 2. 对每个子数组进行过滤,确保被 p 整除的元素个数不超过 k。
 * 3. 使用 Set 来存储这些子数组的字符串表示,确保唯一性。
 * 4. 返回 Set 的大小。
 */
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;

public class Solution {
    public int countDistinctSubarrays(int[] nums, int k, int p) {
        Set<String> uniqueSubarrays = new HashSet<>();
        IntStream.range(0, nums.length).forEach(i ->
            IntStream.rangeClosed(i, nums.length - 1).forEach(j -> {
                int[] subarray = Arrays.copyOfRange(nums, i, j + 1);
                long count = Arrays.stream(subarray).filter(x -> x % p == 0).count();
                if (count <= k) uniqueSubarrays.add(Arrays.toString(subarray));
            })
        );
        return uniqueSubarrays.size();
    }
}

解释

方法:

这个题解利用了两步主要的算法技巧来解决问题:首先,它使用了双指针技术来计算所有可能的符合条件的子数组的数量。具体来说,外层循环遍历数组,内层循环扩展直到子数组中可以被p整除的元素数量超过k为止。在每次外层循环中,计算从当前索引起始的、满足条件的子数组数量,并逐步减少被p整除的元素的计数。其次,利用后缀数组来识别重复的子数组。构建后缀数组并计算最长公共前缀(LCP),从而可以快速确定哪些子数组是重复的。最后,将找到的总子数组数减去重复的子数组数,得到答案。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
在双指针技术中,如何确保在移动内层循环的指针j时,正确地更新和管理可被p整除的元素的计数?
在双指针技术中,指针 j 负责扩展子数组的长度直到子数组中可以被 p 整除的元素数量超过 k 为止。为了准确地管理可被 p 整除的元素计数(cnt),每次 j 向右移动一个位置时,首先检查即将包含进子数组的元素 nums[j] 是否能被 p 整除。如果可以,cnt 增加 1。当指针 i 向右移动,即准备考虑下一个新的子数组起点时,需要从 cnt 中减去 nums[i] 的贡献(如果 nums[i] % p == 0,则减 1)。这样,通过逐步调整 cnt,保证了每次内层循环中 cnt 的值都正确地反映了从 i 到 j 子数组中可以被 p 整除的元素数量。
🦆
题解中提到使用后缀数组来识别重复的子数组,请问如何通过后缀数组和最长公共前缀(LCP)来识别和计算重复的子数组数量?
在使用后缀数组和最长公共前缀(LCP)来识别重复的子数组时,首先通过构建后缀数组(suffix array)对所有子数组的起点进行排序。然后,计算相邻后缀(即排序后的子数组)之间的最长公共前缀(LCP)。LCP 数组中的每个值表示排序后相邻的两个子数组的最长公共前缀的长度。为了计算重复的子数组数量,我们考虑后缀数组中的每个元素 sa[i] 和它的右边界 right[sa[i]](即从 sa[i] 开始的子数组能延伸到的最远位置)。重复子数组的长度至少为 min(height[i], right[sa[i]] - sa[i]),其中 height[i] 是 sa[i] 与 sa[i-1] 的 LCP。通过累加这些值,可以得到所有重复子数组的总长度,从而计算出重复的子数组数量。
🦆
在使用后缀数组处理时,初始化排名数组为字符数组时,如何处理整数数组?是否需要将整数转换为字符,或是使用其他方法?
在处理整数数组时,不需要将整数转换为字符。排名数组(rank array)初始化时通常是基于数组元素的值来排序。对于整数数组来说,可以直接使用整数的值作为排名。初始排名可以通过将数组元素及其索引位置放入一个列表,然后根据元素的值对此列表进行排序来得到。排序后,元素的新位置(索引)在列表中即代表了它的排名。这种方法避免了将整数转换为字符的步骤,直接利用整数本身的值进行操作,更为直接和高效。

相关问题