HTTP 访问日志峰值请求数(2026.11.08)
2026年最新华为OD机考题库(按考点分类): 2026年最新华为OD机考题库
题目描述:
网关为每个到达的 HTTP 请求记录一行访问日志(含方法、路径、时间戳)。给定按到达顺序排列的日志与窗口大小 windowMs,以每条请求的时间戳作为窗口左端点 t,求半开窗口 [t, t+windowMs) 内最多包含多少条请求。
输入描述
输入为一个字符串,格式为 “windowMs METHOD PATH TIMESTAMP METHOD PATH TIMESTAMP …”:首字段为 windowMs,其后每三个字段为一条请求(METHOD、PATH、TIMESTAMP),字段之间以单个空格分隔。TIMESTAMP 按到达顺序非递减。
数据范围
0 <= windowMs <= 10^90 <= TIMESTAMP <= 10^9,且按到达顺序非递减(允许同一毫秒有多条请求)METHOD、PATH均不含空格
输出描述
返回一个整数:所有以请求时间戳为左端点的半开窗口 [t, t+windowMs) 中包含请求数的最大值;windowMs = 0 或无请求时返回 0。
样例
用例输入 1
"1000 GET /api/users 1000 GET /api/users 1200 POST /login 1500 GET /api/users 1800 GET /api/users 2050"
用例输出 1
4
用例解释 1
时间戳为 [1000, 1200, 1500, 1800, 2050],以左端 1000 的窗口 [1000, 2000) 包含 4 条请求。
用例输入 2
"1000 GET /api/users 1000 POST /login 1000 GET /api/orders 1000 GET /api/users 2000 GET /api/users 2500"
用例输出 2
3
用例解释 2
前 3 条请求的时间戳都是 1000,窗口 [1000, 2000) 包含 3 条请求(2000 落在右端点,半开区间不包含)。
用例输入 3
"0 GET /api/users 1000 GET /api/users 2000"
用例输出 3
0
用例解释 3
windowMs = 0,半开窗口为空,返回 0。
来源
本题来源于网络、非官方材料、仅供学习交流,侵删
🧠 解题思路
第一步:题意转化
每条请求只关心它的时间戳,方法和路径对答案没有影响。所以先把字符串按空格切开:第 0 个字段是 windowMs,之后每 3 个字段是一条请求,其中第 3 个字段是时间戳。把所有时间戳按原顺序放进数组 times。
因为题目保证时间戳按到达顺序非递减,所以 times 是一个有序(允许相等)的数组。
第二步:为什么不能对每个左端点都从头数
如果对每个左端点 t 都扫描整个数组,统计满足 t <= times[j] < t + windowMs 的 j,时间复杂度是 O(n^2)。当请求很多时会超时。
注意到窗口右端点只会随着左端点向右移动而单调向右移动:左端点变大时,t + windowMs 也变大,原来在窗口里的请求不会因为左端点右移而需要重新从更左边找。因此可以用双指针。
第三步:双指针维护半开窗口
用 left 表示当前窗口左端点对应的请求下标,right 表示第一个不满足 times[right] < times[left] + windowMs 的下标。
- 初始 left = 0,right = 0。
- 对每个 left,如果 right < left,先把 right 拉到 left(保证窗口至少包含左端点本身)。
- 然后不断让 right 右移,直到 right 越界或 times[right] >= times[left] + windowMs。
- 此时窗口 [left, right) 内的请求数就是 right - left,用它更新答案。
因为 right 只增不减,整个过程每个下标最多被访问两次,时间复杂度 O(n)。
第四步:特殊规则
- windowMs = 0 时,半开窗口 [t, t) 为空,答案固定为 0。
- 没有请求时也返回 0。
- 右端点是开区间:时间戳恰好等于 t + windowMs 的请求不算进窗口。
用样例 1 走一遍
输入解析后 windowMs = 1000,times = [1000, 1200, 1500, 1800, 2050]。
| left | times[left] | 窗口右界 times[left]+1000 | right 移动后 | 窗口内数量 right-left | best |
|---|---|---|---|---|---|
| 0 | 1000 | 2000 | 4(2050 >= 2000 停止) | 4 | 4 |
| 1 | 1200 | 2200 | 5 | 4 | 4 |
| 2 | 1500 | 2500 | 5 | 3 | 4 |
| 3 | 1800 | 2800 | 5 | 2 | 4 |
| 4 | 2050 | 3050 | 5 | 1 | 4 |
最终输出 4。
⚠️ 易错点
- windowMs = 0 时必须直接返回 0,不能把窗口当成包含左端点。
- 右端点是开区间,时间戳等于 t + windowMs 的请求不能计入。
- 时间戳可能相同,双指针移动条件要用 times[right] < times[left] + windowMs,不能写成 <=。
- 时间戳和 windowMs 最大到 10^9,相加可能超过 32 位有符号整数范围,C++/Java 中应使用 long long 或 long 保存时间戳与窗口和。
- 解析时每 3 个字段一条请求,时间戳是第 3 个字段,不要错取方法或路径。
- 没有请求时 parts 只有 windowMs 一个字段,应返回 0。
⏱️ 复杂度
时间复杂度:O(L),L 是输入字符串的长度。切分字符串扫描一遍;双指针中 left、right 都只往右走,每条请求最多被两个指针各经过一次。
空间复杂度:O(L),需要保存切分后的字段和全部时间戳。
💻 参考代码(Java)
class Solution {
public int peakRequestCount(String log) {
String[] parts = log.trim().split("\\s+");
if (parts.length == 0 || parts[0].isEmpty()) return 0;
long window = Long.parseLong(parts[0]);
int m = (parts.length - 1) / 3;
long[] times = new long[m];
// 每三个字段是一条请求,第三个字段是时间戳
for (int i = 0; i < m; i++) {
times[i] = Long.parseLong(parts[1 + i * 3 + 2]);
}
if (window == 0 || m == 0) return 0;
int best = 0, right = 0;
// 时间戳非递减,双指针维护半开窗口
for (int left = 0; left < m; left++) {
if (right < left) right = left;
while (right < m && times[right] < times[left] + window) right++;
best = Math.max(best, right - left);
}
return best;
}
}
💻 参考代码(C++)
class Solution {
public:
int peakRequestCount(string log) {
vector<string> parts;
string cur;
for (char c : log) {
if (c == ' ') {
if (!cur.empty()) { parts.push_back(cur); cur.clear(); }
} else {
cur += c;
}
}
if (!cur.empty()) parts.push_back(cur);
if (parts.empty()) return 0;
long long window = stoll(parts[0]);
vector<long long> times;
// 每三个字段是一条请求,第三个字段是时间戳
for (int i = 1; i + 2 < (int)parts.size(); i += 3) {
times.push_back(stoll(parts[i + 2]));
}
if (window == 0 || times.empty()) return 0;
int best = 0, right = 0, n = times.size();
// 时间戳非递减,双指针维护半开窗口
for (int left = 0; left < n; ++left) {
if (right < left) right = left;
while (right < n && times[right] < times[left] + window) ++right;
best = max(best, right - left);
}
return best;
}
};
💻 参考代码(Python)
class Solution:
def peakRequestCount(self, log: str) -> int:
parts = log.split()
if not parts:
return 0
window = int(parts[0])
times = []
# 每三个字段是一条请求,第三个字段是时间戳
i = 1
while i + 2 < len(parts):
times.append(int(parts[i + 2]))
i += 3
if window == 0 or not times:
return 0
best = 0
right = 0
n = len(times)
# 时间戳非递减,双指针维护半开窗口 [times[left], times[left]+window)
for left in range(n):
if right < left:
right = left
while right < n and times[right] < times[left] + window:
right += 1
best = max(best, right - left)
return best
💻 参考代码(JavaScript)
class Solution {
peakRequestCount(log) {
const parts = log.trim().split(/\s+/).filter(s => s.length > 0);
if (parts.length === 0) return 0;
const window = Number(parts[0]);
const times = [];
// 每三个字段是一条请求,第三个字段是时间戳
for (let i = 1; i + 2 < parts.length; i += 3) {
times.push(Number(parts[i + 2]));
}
if (window === 0 || times.length === 0) return 0;
let best = 0, right = 0;
const n = times.length;
// 时间戳非递减,双指针维护半开窗口
for (let left = 0; left < n; left++) {
if (right < left) right = left;
while (right < n && times[right] < times[left] + window) right++;
best = Math.max(best, right - left);
}
return best;
}
}
module.exports = Solution;
597



被折叠的 条评论
为什么被折叠?



