以下为题目描述
问题描述
小U拿到了一组排列,她想知道有多少个子区间满足区间内部的数构成一个排列。一个区间的排列是指:该区间的数包含从 11 到 kk 的每个数,并且每个数恰好出现一次,这个区间的长度为 kk。
例如,对于数组 [2, 1, 5, 3, 4],其中区间 [2, 2],[1,2] 和 [1, 5] 都是排列。
测试样例
样例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
我的解答如下
import java.util.HashSet;
import java.util.Set;
public class Main {
public static int solution(int n, int[] a) {
int count = 0;
// 遍历所有可能的起始位置
for (int l = 0; l < n; l++) {
Set<Integer> set = new HashSet<>();
int minVal = Integer.MAX_VALUE;
int maxVal = Integer.MIN_VALUE;
// 从起始位置开始,尝试扩展区间
for (int r = l; r < n; r++) {
// 如果出现重复元素,区间不可能构成排列,退出当前循环
if (set.contains(a[r])) {
break;
}
set.add(a[r]);
minVal = Math.min(minVal, a[r]);
maxVal = Math.max(maxVal, a[r]);
int cnt = set.size();
int k = r - l + 1;
// 检查是否满足排列条件
if (cnt == k && minVal == 1 && maxVal == k) {
count++;
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(solution(5, new int[]{2, 1, 5, 3, 4}) == 3);
System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}) == 5);
System.out.println(solution(4, new int[]{4, 3, 2, 1}) == 4);
}
}
代码解释
这道题要求统计数组中有多少个子区间满足以下条件:
• 子区间的长度为 k。
• 子区间内的数字恰好包含从 1 到 k 的所有整数,且每个数只出现一次。
思路:
我们需要遍历所有可能的子区间,检查每个子区间是否满足条件。
为了优化时间复杂度,我们在遍历的过程中,维护以下信息:
• 一个集合 set,用于存储当前子区间内的唯一元素。
• 当前子区间内元素的最小值 minVal 和最大值 maxVal。
• 对于每个子区间 [l, r],我们检查以下条件:
1. 元素个数等于子区间长度: cnt == k,其中 cnt 是集合中元素的数量,k 是子区间长度 r - l + 1。
2. 最大值和最小值的差满足连续性: maxVal - minVal + 1 == k。
3. 这意味着子区间内的元素是连续的整数序列,没有缺失的数字。
4. 如果上述条件都满足,则当前子区间是一个符合要求的排列,计数器 count 增加。
代码详解:
• 外层循环:
for (int l = 0; l < n; l++)
遍历所有可能的起始位置 l
• 内层循环:
for (int r = l; r < n; r++)
从起始位置 l 开始,尝试扩展到位置 r,形成子区间 [l, r]
• 集合判断:
if (set.contains(a[r])) break;
如果当前元素已经在集合中出现过,说明存在重复元素,子区间不可能构成排列,退出当前循环
• 更新集合和最值:
set.add(a[r]);
将当前元素加入集合。
minVal = Math.min(minVal, a[r]);
更新最小值
maxVal = Math.max(maxVal, a[r]);
• 更新最大值。
• 计算子区间长度和元素数量:
int cnt = set.size();
当前子区间内的唯一元素数量。
int k = r - l + 1;
当前子区间的长度。
• 检查排列条件:
if (cnt == k && maxVal - minVal + 1 == k)
如果元素数量等于子区间长度,且最大值和最小值的差满足连续性,则计数器 count 增加。
但是,我一开始在写的时候,在检查排列条件时,没有判断好,仅检查了以下两点:
• 子区间内的元素个数等于子区间长度(cnt == k)。
• 最大值和最小值的差加一等于子区间长度(maxVal - minVal + 1 == k)。
但是,这无法保证子区间内的数字是从 1 到 k,因此会导致错误的结果。为了满足题目要求,需要修改判断条件,确保子区间内的数字是从 1 到 k,且每个数字恰好出现一次。修改后如下:
if (cnt == k && minVal == 1 && maxVal == k) {
count++;
}