题目
日志统计(编程题)
讲解
这个讲解感觉比较通俗易懂。
题目描述
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有 N行。其中每一行的格式是 ts id
,表示在 ts时刻编号 id 的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为 D的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻 T满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
输入格式
第一行包含三个整数 N、D 和 K。
以下 N 行每行一条日志,包含两个整数 ts 和 id。
输出格式
按从小到大的顺序输出热帖 id。每个 id 一行。
样例
输入数据 1
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
输出数据 1
1
3
提示
- 对于 50% 的数据,1≤K≤N≤1000。
- 对于 100% 的数据,1≤K≤N≤,0≤id,ts≤。
思路
双指针,滑动窗口,给id分组(按照时间从小到大的顺序)
使用pair数组分组,id虽然后输入,但要放在first,这样以id为优先排序
比如样例的
0 1
0 10
10 10
10 1
9 1
100 3
100 3
按照id为first进行排序后
id ts
1 0
1 9
1 10
3 100
3 100
10 0
10 10
通过循环得到每个id的begin和end,然后用自定义的judge函数进行判断这个id是否是热帖。
judge函数:核心是双指针
-
双指针窗口初始化:
-
快指针
i
与慢指针j
初始指向当前id的起始位置begin
-
sum
记录窗口内有效点赞数,初始为0
-
-
窗口滑动逻辑:
-
窗口扩张 :快指针
i
右移,sum++
累计点赞数 -
阈值检测 :当
sum >= k
时,检查窗口时间跨度p[i].y - p[j].y
:-
若时间差
< d
:满足热帖条件,直接返回true
-
否则:慢指针
j
右移缩小窗口,sum--
保持窗口大小
-
-
终止条件 :快指针
i
超出当前id的end
位置
-
-
关键性质:
-
通过单调滑动窗口 确保时间复杂度为
O(n)
-
依赖时间有序性(因预处理排序保证)
-
代码
cpp
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int N=1e5+5;
pair<int,int>p[N];
bool judge(int begin, int end);
#define x first
#define y second
int n,d,k;
int main() {
cin>>n>>d>>k;
for(int i=0;i<n;i++)
{
cin>>p[i].y>>p[i].x;//让后输入的id放到pair数组的first位置,这样排序时以id优先
}
sort(p,p+n);
int i=0;
while(i<n)
{
int id=p[i].x;
int begin=i,end;
while(id==p[i].x&&i<n)//找到每组id的起始位置和终止位置
{
end=i;i++;
}
if(judge(begin,end))//对这个id进行判断是否为热帖
{
cout<<p[end].x<<endl;
}
}
return 0;
}
bool judge(int begin, int end) {
int i=begin,j=begin;
int sum=0;
while(j<=i&&i<=end)//使用双指针
{
sum++;
if(sum>=k)
{
if(p[i].y-p[j].y<d)return true;
else{
sum--;j++;
}
}
i++;
}
return false;
}