【OD机试题解法笔记】查找接口成功率最优时间段

题目描述

服务之间交换的接口成功率作为服务调用关键质量特性,某个时间段内的接口失败率使用一个数组表示,数组中每个元素都是单位时间内失败率数值,数组中的数值为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)的最长子数组。具体步骤如下:

  1. 初始化变量

    • i:窗口左边界的前一个位置(初始为-1,方便计算窗口长度)。
    • j:窗口右边界(从0开始遍历数组)。
    • sum:当前窗口内所有元素的和(用于计算平均值)。
    • maxLen:记录最长有效窗口的长度(初始为0)。
    • result:存储所有最长有效窗口的下标对(格式为"begin-end")。
  2. 扩展窗口右边界

    • 遍历数组,将当前元素list[j]加入sum,使窗口右边界j向右移动。
    • 计算当前窗口(i+1j)的平均值:sum/(j-i)(窗口长度为j-i)。
  3. 判断窗口是否有效

    • 若平均值≤minAverageLost:
      • 若当前窗口长度等于maxLen,则将该窗口的下标对加入result
      • 若当前窗口长度大于maxLen,则更新maxLen为当前长度,并重置result为仅包含该窗口的下标对。
    • 若平均值>minAverageLost:
      • 移动左边界i向右(缩小窗口),同时从sum中减去list[i],直到窗口平均值≤minAverageLost。
  4. 处理结果

    • result为空,输出"NULL";否则按顺序输出所有下标对。

示例解析(第二个用例)

  • 输入:minAverageLost=2,数组[0,0,100,2,2,99,0,2]
  • 过程:
    • j=0:窗口[0],平均值0≤2,maxLen=1result=["0-0"]
    • j=1:窗口[0,0],平均值0≤2,长度2>1,maxLen=2result=["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();
});
相关推荐
杰克尼1 小时前
11. 盛最多水的容器
算法·leetcode·职场和发展
【上下求索】3 小时前
学习笔记090——Ubuntu 中 UFW 防火墙的使用
笔记·学习·ubuntu
NeRF_er3 小时前
STORM代码阅读笔记
大数据·笔记·storm
UQWRJ3 小时前
菜鸟教程Linux ViVimYumApt笔记
linux·运维·笔记
技术思考者3 小时前
Leetcode - 反转字符串
数据结构·算法·leetcode
SKYDROID云卓小助手4 小时前
无人设备遥控器之多设备协同技术篇
网络·人工智能·嵌入式硬件·算法·信号处理
wb1895 小时前
企业WEB应用服务器TOMCAT
运维·前端·笔记·tomcat·云计算
熬了夜的程序员5 小时前
【华为机试】34. 在排序数组中查找元素的第一个和最后一个位置
数据结构·算法·华为od·华为·面试·golang