「134.区间内排列的数量」
问题描述 小U拿到了一组排列,对于给定的数组,需要确定其中有多少个子区间满足区间内部的数能构成一个排列。这里所说的一个区间构成排列是指:该区间内的数包含从1到k的每个数,并且每个数恰好出现一次,同时这个区间的长度为k。
思路解析
- 理解排列子区间概念 首先要明确什么是满足条件的排列子区间。比如给定数组[2, 1, 5, 3, 4],像[2, 1]这个区间,它包含了1和2这两个数,且每个数只出现一次,长度为2,所以是满足条件的排列子区间;同理[5, 3, 4]包含了3、4、5且各出现一次,长度为3,也是排列子区间等。我们的任务就是找出数组中所有这样的子区间个数。
- 确定判断方法 为了判断一个子区间是否是满足条件的排列子区间,我们可以利用一个计数数组来记录每个数在子区间内出现的次数。从子区间的起始位置开始,依次遍历到结束位置,每遇到一个数,就在计数数组中对应的位置加1。当遍历完整个子区间后,如果计数数组中从1到子区间长度的每个位置的值都为1,那么这个子区间就是满足条件的排列子区间。
- 遍历所有子区间 要找出所有满足条件的子区间,就需要遍历数组的所有可能子区间。可以通过两层循环来实现,外层循环控制子区间的起始位置,内层循环控制子区间的结束位置,这样就能遍历到所有可能的子区间组合,然后对每个子区间进行是否为排列子区间的判断。
解题步骤
- 初始化计数数组:创建一个长度为数组长度加1的计数数组,初始值都设为0,用于记录每个数在子区间内的出现次数。
- 两层循环遍历子区间:
- 外层循环:从数组的第一个位置开始,依次作为子区间的起始位置,直到数组的倒数第二个位置(因为以最后一个位置为起始位置的子区间只有它自身这一种情况,已经在前面的遍历中包含了)。
- 内层循环:对于每个起始位置,从该起始位置开始,依次向后延伸作为子区间的结束位置,直到数组的最后一个位置。
- 判断子区间是否为排列子区间:
- 对于每个子区间,在进入内层循环时,先将计数数组的值全部重置为0。
- 然后从子区间的起始位置到结束位置遍历数组中的数,每遇到一个数,就在计数数组中对应的位置加1。
- 当遍历完子区间后,检查计数数组中从1到子区间长度的每个位置的值是否都为1,如果是,则说明该子区间是满足条件的排列子区间,此时记录满足条件的子区间个数加1。
- 返回结果:将记录的满足条件的排列子区间个数返回作为最终答案。
代码实现
def count_permutation_subintervals(n, a):
count = 0
for i in range(n - 1):
count_array = [0] * (n + 1)
for j in range(i, n):
count_array[a[j]] += 1
if all(count_array[k] == 1 for k in range(1, j - i + 2)):
count += 1
return count
代码解读
- 首先定义了函数
count_permutation_subintervals,它接受数组的长度n和数组a作为参数。 - 在函数内部,先初始化了一个变量
count用于记录满足条件的排列子区间个数,初始值设为0。 - 然后通过外层循环
for i in range(n - 1)来遍历数组作为子区间的起始位置。对于每个起始位置i,创建一个新的计数数组count_array并初始化为全0,用于记录在当前子区间内每个数的出现次数。 - 接着通过内层循环
for j in range(i, n)来遍历从起始位置i到数组末尾的位置作为子区间的结束位置。在每次内层循环中,先将当前位置j对应的数在计数数组count_array中的值加1,然后通过if all(count_array[k] == 1 for k in range(1, j - i + 2))来判断当前子区间是否是满足条件的排列子区间,如果是,则将count的值加1。 - 最后,函数返回
count的值,即满足条件的排列子区间个数。
复杂度分析
- 时间复杂度:
- 外层循环遍历数组作为子区间起始位置,共执行
n - 1次。内层循环对于每个起始位置,最多执行n次。在每次内层循环中,判断子区间是否为排列子区间的操作时间复杂度相对较小,可以看作常数时间。所以总体时间复杂度为,其中n是数组的长度。
- 外层循环遍历数组作为子区间起始位置,共执行
- 空间复杂度:
- 主要的空间消耗在于计数数组
count_array,其长度为数组长度加1,所以空间复杂度为。
- 主要的空间消耗在于计数数组