九万五千一的尺取法笔记

尺取法的概念

把两重循环转化为一重循环,从而把时间复杂度从 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从尾扫到头
}

把循环指针 ij 称为「扫描指针」,在尺取法中,这两个指针 ij,有两种扫描方向:

  • 反向扫描。ij 方向相反,i 从头到尾,j 从尾到头,在中间相会。也可以把反向扫描的 ii、jj 指针称为「左右指针」。

  • 同向扫描。ij 方向相同,都从头到尾,但是速度不一样,比如可以让 j 跑在 ii 前面。也可以把同向扫描的 ij 指针称为「快慢指针」,此时由于 iij 速度不同,ij 之间在序列上产生了一个大小可变的「滑动窗口」,这是尺取法的优势,有灵活的应用。

    接下来是题目的练习

    回文判定

#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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值