题目描述
服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示,数组中每个元素都是单位时间内失败率数值,数组中的数值为0~100的整数,给定一个数值(minAverageLost)表示某个时间段内平均失败率容忍值,即平均失败率小于等于minAverageLost,找出数组中最长时间段,如果未找到则直接返回NULL。
输入描述
输入有两行内容,第一行为{minAverageLost},第二行为{数组},数组元素通过空格(" ")分隔,minAverageLost及数组中元素取值范围为0~100的整数,数组元素的个数不会超过100个。
输出描述
找出平均值小于等于minAverageLost的最长时间段,输出数组下标对,格式{beginIndex}-{endIndx}(下标从0开始),如果同时存在多个最长时间段,则输出多个下标对且下标对之间使用空格(" ")拼接,多个下标对按下标从小到大排序。
用例
输入 | 输出 | 说明 |
---|---|---|
1 0 1 2 3 4 | 0-2 | 输入解释:minAverageLost=1,数组[0, 1, 2, 3, 4] 前3个元素的平均值为1,因此数组第一个至第三个数组下标,即0-2 |
2 0 0 100 2 2 99 0 2 | 0-1 3-4 6-7 | 输入解释:minAverageLost=2,数组[0, 0, 100, 2, 2, 99, 0, 2] 通过计算小于等于2的最长时间段为: 数组下标为0-1即[0, 0],数组下标为3-4即[2, 2],数组下标为6-7即[0, 2], 这三个部分都满足平均值小于等于2的要求,因此输出0-1 3-4 6-7 |
思考
题目要求找最长时间段且平均失败率不高于平均失败率。首先"时间段"表示是连续的时间窗口,"最长"要求我们让这个窗口在满足约束条件时最大,显然和求字符串中最长不重复子串的思路相同,用滑动窗口思想求解,时间复杂度O(N)。题目中给出的数组长度不大于100,如果滑动窗口写得不熟练,可以直接暴力两重循环枚举窗口左右边界 i 和 j 求解,这样复杂度O(n^2) 也能通过所有用例。
滑动窗口技巧 :左右指针定义为左开右闭区间(i, j]
,开区间表示边界i
不包含在窗口中,每次 i++
,都会移除 i
对应的窗口中的元素,右边界 j
是闭区间边界,表示包含在窗口中,每次 j++
都会移入新的元素到窗口中。
算法过程(滑动窗口)
滑动窗口算法的核心是通过维护一个连续的子数组窗口,动态调整窗口的左右边界,以找到满足条件(平均失败率≤minAverageLost)的最长子数组。具体步骤如下:
-
初始化变量
i
:窗口左边界的前一个位置(初始为-1,方便计算窗口长度)。j
:窗口右边界(从0开始遍历数组)。sum
:当前窗口内所有元素的和(用于计算平均值)。maxLen
:记录最长有效窗口的长度(初始为0)。result
:存储所有最长有效窗口的下标对(格式为"begin-end")。
-
扩展窗口右边界
- 遍历数组,将当前元素
list[j]
加入sum
,使窗口右边界j
向右移动。 - 计算当前窗口(
i+1
到j
)的平均值:sum/(j-i)
(窗口长度为j-i
)。
- 遍历数组,将当前元素
-
判断窗口是否有效
- 若平均值≤minAverageLost:
- 若当前窗口长度等于
maxLen
,则将该窗口的下标对加入result
。 - 若当前窗口长度大于
maxLen
,则更新maxLen
为当前长度,并重置result
为仅包含该窗口的下标对。
- 若当前窗口长度等于
- 若平均值>minAverageLost:
- 移动左边界
i
向右(缩小窗口),同时从sum
中减去list[i]
,直到窗口平均值≤minAverageLost。
- 移动左边界
- 若平均值≤minAverageLost:
-
处理结果
- 若
result
为空,输出"NULL";否则按顺序输出所有下标对。
- 若
示例解析(第二个用例)
- 输入:
minAverageLost=2
,数组[0,0,100,2,2,99,0,2]
- 过程:
j=0
:窗口[0]
,平均值0≤2,maxLen=1
,result=["0-0"]
。j=1
:窗口[0,0]
,平均值0≤2,长度2>1,maxLen=2
,result=["0-1"]
。j=2
:加入100后平均值≈34>2,左移i
至1,窗口变为[100]
,仍不满足,继续左移i
至2,窗口为空。j=3
:窗口[2]
,平均值2≤2,长度1<2,不更新。j=4
:窗口[2,2]
,平均值2≤2,长度2=2,result=["0-1", "3-4"]
。j=5
:加入99后平均值≈50.5>2,左移i
至5,窗口为空。j=6
:窗口[0]
,长度1<2,不更新。j=7
:窗口[0,2]
,平均值1≤2,长度2=2,result=["0-1", "3-4", "6-7"]
。
- 输出:
0-1 3-4 6-7
。
该算法通过一次遍历即可完成,时间复杂度为O(n)(每个元素最多被加入和移出窗口各一次)。
参考代码
javascript
function solution() {
const minAvg = parseInt(readline());
const list = readline().split(' ').map(Number);
let result = [];
let n = list.length, maxLen = 0, sum = 0;
for (let i = -1, j = 0; j < n; j++) {
sum += list[j];
if (sum / (j-i) <= minAvg) {
if (maxLen === j-i) {
result.push(`${i+1}-${j}`);
} else if (maxLen < j-i) {
maxLen = j-i;
result = [`${i+1}-${j}`];
}
continue;
}
while (i <= j && sum / (j-i) > minAvg) {
i++;
sum -= list[i];
}
}
console.log(result.length === 0 ? "NULL" : result.join(' '));
}
const cases = [
`1
0 1 2 3 4`,
`2
0 0 100 2 2 99 0 2`,
];
let caseIndex = 0;
let lineIndex = 0;
const readline = (function () {
let lines = [];
return function () {
if (lineIndex === 0) {
lines = cases[caseIndex]
.trim()
.split("\n")
.map((line) => line.trim());
}
return lines[lineIndex++];
};
})();
cases.forEach((_, i) => {
caseIndex = i;
lineIndex = 0;
solution();
});