题目
题目描述
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。
其中每一行的格式是:ts id。表示在ts时刻编号id的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。
如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
输入
第一行包含三个整数N、D和K。
以下N行每行一条日志,包含两个整数ts和id。
1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000
输出
按从小到大的顺序输出热帖id。每个id一行。
样例输入
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
样例输出
1
3
代码
方法一:
我也不太懂这种是什么方法…
思路就是把读入的数据都放入一个类里,然后按id大小排序,其次再按ts大小排序,然后用下标i和i+k-1进行相关数据的判断
为什么是i+k-1呢?因为我们排序就把就把相同的id放在一块了,这样只要找i的第k个就可以满足赞不少于k个了。比如i=1时,i+k-1=2,id=1,他们的时间差小于d,但是前面i=0时id=1已经满足条件输出了,flag[id=1]被标记为1,所以就继续循环,判断i=2和i+k-1=3…
|--|
i=1 i+k-1
|--|
|--|
i 0 1 2 3 4 5 6
id 1 1 1 3 3 10 10
ts 0 9 10 100 100 0 10
package 日志统计;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int d = in.nextInt();//时间段
int k = in.nextInt();//不少于k个赞
Order order[] = new Order[n];
int flag[] = new int[100005];//标记是否已经满足过条件【避免重复输出】
for(int i=0;i<n;i++) {
order[i] = new Order(in.nextInt(),in.nextInt());
}
Arrays.sort(order);
/**
* 排序好的order:
* i i+k-1
* |--|
* i i+k-1
* |--|
* ...
* i 0 1 2 3 4 5 6
* id 1 1 1 3 3 10 10
* ts 0 9 10 100 100 0 10
*
*/
for(int i=0;i<n;i++) {
/**
* i+k-1表示第k个的id,
*
* i+k-1<n 防止数组越界
*
* order[i].id==order[i+k-1].id 看第i个和第i+k-1个的id是否相同
*
* order[i+k-1].ts-order[i].ts<d 看第i个和第i+k-1个之间的ts差有没有超出给出的时间范围d
*
* flag[order[i].id]==0 检查第i个数据的id有没有已经满足条件输出过
*/
if(i+k-1<n&&order[i].id==order[i+k-1].id&&order[i+k-1].ts-order[i].ts<d&&flag[order[i].id]==0) {
System.out.println(order[i].id);
flag[order[i].id] = 1;
}
}
in.close();
}
}
class Order implements Comparable<Order>{
int ts;
int id;
public Order(int ts, int id) {
this.ts = ts;
this.id = id;
}
public int compareTo(Order o) {//先按id从小到大排序,如果相同就按ts从小到大排序
if(this.id==o.id) {
return this.ts-o.ts;
}
return this.id-o.id;
}
}
方法二:双指针/尺取法/窗口滑动
这个是按ts从小到大排序,思路在代码里
看不懂代码可以看看下面这个
/**
* ①第一次循环right=0
* l&r
* i 0 1 2 3 4 5 6
* ts 0 0 9 10 10 100 100
* id 1 10 1 10 1 3 3
* count[1]=1;
* ^
*
* ② 第二次循环right=1
* l r
* i 0 1 2 3 4 5 6
* ts 0 0 9 10 10 100 100
* id 1 10 1 10 1 3 3
* count[1]=1;
* count[10]=1;
* ^
*
* ③第三次循环right=2
* l r
* i 0 1 2 3 4 5 6
* ts 0 0 9 10 10 100 100
* id 1 10 1 10 1 3 3
* count[1]=1+1;
* ^
* count[10]=1;
* 输出id=1(满足条件)并做标记
*
* ④第四次循环right=3
* l r
* i 0 1 2 3 4 5 6
* ts 0 0 9 10 10 100 100
* id 1 10 1 10 1 3 3
* count[1]=1+1;
* count[10]=1+1;
* 这时时间差等于d,开始移动left,进入while循环
*
* l r
* i 0 1 2 3 4 5 6
* ts 0 0 9 10 10 100 100
* id 1 10 1 10 1 3 3
* count[1]=1+1-1;
* ^
* count[10]=1+1;
* 这时时间差还是等于d,继续移动left
*
* l r
* i 0 1 2 3 4 5 6
* ts 0 0 9 10 10 100 100
* id 1 10 1 10 1 3 3
*
* count[1]=1+1-1;
* count[10]=1+1-1;
* ^
* 这时时间差小于d,跳出while循环
*
* ⑤第五次循环right=4
* l r
* i 0 1 2 3 4 5 6
* ts 0 0 9 10 10 100 100
* id 1 10 1 10 1 3 3
*
* count[1]=1+1-1-1+1;
* ^
* count[10]=1+1-1;
* ......
*
*/
package 日志统计;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
public class Main4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int d = in.nextInt();//时间段
int k = in.nextInt();//不少于k个赞
Node node[] = new Node[n];
int flag[] = new int[100005];
int count[] = new int[100005];
for(int i=0;i<n;i++) {
node[i] = new Node(in.nextInt(),in.nextInt());
}
Arrays.sort(node);
int id;
for(int left=0,right=0;right<n;right++) {//这就像一个区间[legt,right],移动right使区间不断扩大
id = node[right].id;
count[id]++;
while(node[right].ts-node[left].ts>=d) { //当node[left]和node[right]之间的时间差大于等于d时,就移动left缩小区间,并把不再在[legt,right]范围里id的点赞数消去
count[node[left].id]--;
left++;
}
if(count[id]>=k&&flag[id]==0) {//大于等于k个赞并没有输出过
System.out.println(id);
flag[id] = 1;
}
}
}
}
class Node implements Comparable<Node>{
int ts;
int id;
public Node(int ts, int id) {
this.ts = ts;
this.id = id;
}
public int compareTo(Node o) {//按ts从小到大排序
return this.ts-o.ts;
}
}