尺取法的概念
把两重循环转化为一重循环,从而把时间复杂度从 O(n*2) 提高到 O(n)。
for (int i = 0, j = n - 1; i < j; i++, j--) { ...... }
这里看一下他的while写法
//用while实现: int i = 0, j = n - 1; while (i < j) { //i和j在中间相遇。这样做还能防止i、j越界 ...... //满足题意的操作 i++; //i从头扫到尾 j--; //j从尾扫到头 }
把循环指针 i 、j 称为「扫描指针」,在尺取法中,这两个指针 i、j,有两种扫描方向:
-
反向扫描。i、j 方向相反,i 从头到尾,j 从尾到头,在中间相会。也可以把反向扫描的 ii、jj 指针称为「左右指针」。
-
同向扫描。i、j 方向相同,都从头到尾,但是速度不一样,比如可以让 j 跑在 ii 前面。也可以把同向扫描的 i、j 指针称为「快慢指针」,此时由于 ii 和 j 速度不同,i 和 j 之间在序列上产生了一个大小可变的「滑动窗口」,这是尺取法的优势,有灵活的应用。
接下来是题目的练习
回文判定
#include <bits/stdc++.h> using namespace std; int main(){ string s; cin >> s; //读一个字符串 bool ans = true; int i = 0, j = s.size() - 1; //双指针 while(i < j){ if(s[i] != s[j]){ ans = false; break; } i++; j--; //移动双指针 } if(ans) cout << "Y" << endl; else cout << "N" << endl; return 0; }
竞赛真题,日志统计
题目描述
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有 N 行。其中每一行的格式是:
ts id
表示在 ts 时刻编号 id的帖子收到一个"赞"。
现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为 D 的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是"热帖"。
具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K 个赞,该帖就曾是"热帖"。
给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。
输入描述
第一行包含三个整数 N,D,KN,D,K。
以下 N 行每行一条日志,包含两个整数 ts 和 id。
输出描述
按从小到大的顺序输出热帖 id。每个 id一行。
样例输入
7 10 2 0 1 0 10 10 10 10 1 9 1 100 3 100 3
样例输出
1 3
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int num[N]; //num[i]:记录id=i的帖子的赞的数量
int flag[N]; //flag[i]:id=i的贴子曾是热帖
struct post{
int id;
int ts;
}p[N]; //记录帖子
int cmp(post x,post y){ return x.ts < y.ts; } //按时间从小到大排序
int main(){
int n,d,k;
cin>>n>>d>>k;
for(int i=0;i<n;i++)
cin>>p[i].ts>>p[i].id;
sort(p,p+n,cmp); //按时间从小到大排序
for(int i=0,j=0;i<n;i++){
num[p[i].id]++;
while(p[i].ts-p[j].ts >= d){
num[p[j].id]--; //随着时间流逝,d之前的每个贴的次数都减1
j++;
}
if(num[p[i].id] >= k) //在区间[i-d,i)上达到k个赞
flag[p[i].id]=1;
}
for(int i=0;i<N;i++)
if(flag[i]==1)
cout<<i<<endl;
return 0;
}