疯狂子数组的统计 | 豆包MarsCode AI刷

120 阅读5分钟

题目解析

题目描述: 给定一个长度为 N 的数组 A,编号从 0 到 N-1。如果一个从索引 i 到 j 的子数组包含一个出现至少 K 次的元素 X,我们就称这个子数组为“疯狂子数组”。现在,你需要计算数组 A 中所有的疯狂子数组的数量。

思路

  1. 滑动窗口:使用滑动窗口技术来高效地计算所有“疯狂子数组”的数量。
  2. 计数器:使用一个计数器 count 来记录当前窗口内每个元素的出现次数。
  3. 左右指针:使用两个指针 left 和 right 来表示滑动窗口的左右边界。
  4. 结果计数:使用一个变量 result 来记录所有“疯狂子数组”的数量。
  5. 有效性标志:使用一个布尔变量 valid 来标记当前窗口内是否有元素出现次数达到或超过 K

图解: 假设数组 A = [1, 5, 2, 5, 2]K = 2

  1. 初始状态:left = 0right = 0count = {1: 1}valid = Falseresult = 0

  2. 右指针移动到 right = 1count = {1: 1, 5: 1}valid = False

  3. 右指针移动到 right = 2count = {1: 1, 5: 1, 2: 1}valid = False

  4. 右指针移动到 right = 3count = {1: 1, 5: 2, 2: 1}valid = True

    • 计算以 right = 3 为结尾的所有子数组数量:result += (N - right) = 5 - 3 = 2
  5. 左指针移动到 left = 1count = {1: 1, 5: 1, 2: 1}valid = False

  6. 右指针移动到 right = 4count = {1: 1, 5: 1, 2: 2}valid = True

    • 计算以 right = 4 为结尾的所有子数组数量:result += (N - right) = 5 - 4 = 1
  7. 左指针移动到 left = 2count = {1: 1, 5: 1, 2: 1}valid = False

  8. 右指针移动到 right = 5count = {1: 1, 5: 1, 2: 2}valid = True

    • 计算以 right = 5 为结尾的所有子数组数量:result += (N - right) = 5 - 5 = 1
  9. 左指针移动到 left = 3count = {1: 1, 5: 1, 2: 1}valid = False

  10. 右指针移动到 right = 6count = {1: 1, 5: 1, 2: 2}valid = True

    • 计算以 right = 6 为结尾的所有子数组数量:result += (N - right) = 5 - 6 = 0

最终结果:result = 5

代码详解

python

def solution(N: int, K: int, A: list) -> int:
    from collections import defaultdict
    
    count = defaultdict(int)
    left = 0
    result = 0
    valid = False
    
    for right in range(N):
        count[A[right]] += 1
        if count[A[right]] == K:
            valid = True
        
        while valid:
            result += (N - right)
            count[A[left]] -= 1
            if count[A[left]] == K - 1:
                valid = False
            if count[A[left]] == 0:
                del count[A[left]]
            left += 1
    
    return result

if __name__ == '__main__':
    print(solution(N=5, K=2, A=[1, 5, 2, 5, 2]) == 5)
    print(solution(N=6, K=3, A=[1, 1, 2, 2, 1, 2]) == 4)
    print(solution(N=4, K=2, A=[4, 4, 4, 4]) == 6)

知识总结

新知识点

  1. 滑动窗口技术:滑动窗口是一种常用的算法技巧,适用于处理数组或字符串中的子数组或子串问题。通过维护一个窗口,可以在 O(N) 时间复杂度内解决问题。
  2. 计数器:使用 defaultdict 来记录当前窗口内每个元素的出现次数,方便快速增删和查询。
  3. 布尔标志:使用布尔变量 valid 来标记当前窗口内是否有元素出现次数达到或超过 K,简化逻辑判断。

学习建议

  1. 理解滑动窗口:滑动窗口技术的核心在于维护一个窗口,通过移动左右指针来调整窗口的大小。理解这一点后,可以解决很多子数组或子串问题。
  2. 熟练使用计数器defaultdict 是 Python 中非常实用的数据结构,可以方便地进行计数操作。多练习使用 defaultdict,可以提高代码的简洁性和效率。
  3. 调试和测试:在编写代码时,多进行调试和测试,确保每个部分的逻辑都正确无误。可以使用简单的测试用例来验证代码的正确性。

问题总结

出现问题的原因

  1. 逻辑错误:在第一次实现中,计算子数组数量的逻辑有误,导致结果不正确。
  2. 边界条件处理不当:在第二次实现中,移动左边界时没有正确处理边界条件,导致部分子数组没有被正确计算。

解决方法

  1. 仔细检查逻辑:在编写代码时,仔细检查每个部分的逻辑,确保没有遗漏或错误。
  2. 增加测试用例:编写更多的测试用例,特别是边界条件和特殊情况,确保代码的鲁棒性。
  3. 调试工具:使用调试工具(如 PyCharm 的调试功能)来逐步执行代码,观察变量的变化,找出问题所在。

可能出现问题的原因及解决办法

  1. 数据结构选择不当:选择合适的数据结构非常重要,不同的数据结构适合不同的场景。如果选择不当,可能导致代码复杂度增加或性能下降。

    • 解决办法:在选择数据结构时,考虑问题的具体需求,选择最合适的结构。
  2. 代码冗余:代码中可能存在冗余的部分,影响可读性和性能。

    • 解决办法:定期重构代码,去除冗余部分,提高代码的简洁性和效率。
  3. 边界条件处理:边界条件是容易出错的地方,需要特别注意。

    • 解决办法:在编写代码时,仔细考虑边界条件,并编写相应的测试用例进行验证。

通过以上总结和反思,希望能够在未来的编程实践中避免类似的问题,提高代码的质量和效率。