134. 区间内排列的数量 题解 | 豆包MarsCode AI刷题

56 阅读3分钟

问题描述

小U拿到了一组排列,她想知道有多少个子区间满足区间内部的数构成一个排列。一个区间的排列是指:该区间的数包含从 1 到 k 的每个数,并且每个数恰好出现一次,这个区间的长度为 k。

例如,对于数组 [2, 1, 5, 3, 4],其中区间 [2, 1][5, 3, 4] 和 [1, 5, 3, 4] 都是排列。

测试样例

样例1:

输入:n = 5 ,a = [2, 1, 5, 3, 4]
输出:3

样例2:

输入:n = 5 ,a = [1, 2, 3, 4, 5]
输出:5

样例3:

输入:n = 4 ,a = [4, 3, 2, 1]
输出:4


思路解析

暴力枚举所有区间

通过枚举所有可能的子区间起点和终点,判断其是否构成一个排列:

  1. 遍历区间起点 start,枚举从起点到数组末尾的所有子区间。

  2. 使用集合 seen 记录子区间内的所有数,确保没有重复元素。

  3. 判断:

    • 区间长度 length 等于集合的大小(无重复元素)。
    • 集合中的最大值等于当前区间的长度(包含 1 到 k 的所有整数)。

优化剪枝

  • 如果当前区间长度大于集合的大小,说明有重复元素,后续区间不可能构成排列,可直接结束当前起点的检查。

复杂度分析

  1. 时间复杂度
    外层起点遍历 O(n),内层终点遍历 O(n),总体为 O(n^2)
  2. 空间复杂度
    使用集合存储子区间的元素,空间复杂度为 O(k),其中 k 为最大区间长度。

Python 实现

def solution(n: int, a: list) -> int:
    count = 0
    
    # 遍历所有可能的起点
    for start in range(n):
        seen = set()
        # 遍历从当前起点开始的子区间
        for end in range(start, n):
            seen.add(a[end])
            length = end - start + 1
            
            # 检查当前区间是否包含完整的 1 到 k
            if length > len(seen):
                break  # 区间有重复元素,剪枝
            if length == len(seen) and length == max(seen):
                count += 1  # 当前区间是排列
    
    return count

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

测试样例解析

样例 1:

输入:n=5,a=[2,1,5,3,4]n = 5, a = [2, 1, 5, 3, 4]n=5,a=[2,1,5,3,4]
过程:

  • 子区间 [2,1][2, 1][2,1]:长度 2,包含 1, 2(有效)。
  • 子区间 [5,3,4][5, 3, 4][5,3,4]:长度 3,包含 3, 4, 5(有效)。
  • 子区间 [1,5,3,4][1, 5, 3, 4][1,5,3,4]:长度 4,包含 1, 3, 4, 5(有效)。

结果:3

样例 2:

输入:n=5,a=[1,2,3,4,5]n = 5, a = [1, 2, 3, 4, 5]n=5,a=[1,2,3,4,5]
过程:所有区间均为排列,结果为 5。

样例 3:

输入:n=4,a=[4,3,2,1]n = 4, a = [4, 3, 2, 1]n=4,a=[4,3,2,1]
过程:每个区间都是排列,结果为 4。


总结

该方法通过暴力枚举并结合剪枝优化,有效计算数组中满足条件的排列区间数,适用于数组规模较小的场景。