尺取法及其应用

尺取法:也可以称为双指针、two pointers,是算法竞赛中一个常用的优化技巧,常用来解决序列的区间问题,操作简单,容易编程。

简单来说,尺取法可以把两重循环转化为一重循环,从而把时间复杂度从 O(n2)​ 提高到 O(n)。

实现方式就是定义一头一尾两个指针,直到两个指针相遇为止。

for (int i = 0, j = n - 1; i < j; i++, j--) {
    ......
}

举几个例子

例题1 反向扫描--回文判定

题目描述

给定一个长度为 n 的字符串 S。请你判断字符串 S 是否回文。

输入描述

输入仅 1 行包含一个字符串 S。

1≤∣S∣≤10^6,保证 S只包含大小写、字母。

输出描述

若字符串 S为回文串,则输出 Y,否则输出 N。

样例输入

abcba

样例输出

Y

 思路简单用双指针很快就能解决

代码如下

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

例题2 正向扫描--日志统计

题目描述

小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有 N 行。其中每一行的格式是:

ts  id

表示在 ts 时刻编号 id的帖子收到一个"赞"。

现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为 DD的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是"热帖"。

具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K 个赞,该帖就曾是"热帖"。

给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。

输入描述

第一行包含三个整数 N,D,K。

以下 N 行每行一条日志,包含两个整数 ts 和 id。

其中1≤KN≤10^5, 0≤ts≤10^5, 0≤id≤10^5。

输出描述

按从小到大的顺序输出热帖 id。每个 id 一行。

样例输入

7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 

样例输出

1
3

思路:按时间从小到大处理每个帖子,当处理到 T 时刻的贴子时,窗口 [T-D, T)之前的帖子等于已经失效了,对当前窗口的统计无用。

  1. 定义 i 指针,它是主循环,遍历随时间而流逝的所有帖子。第 17 行是 i 的 for 循环。
  2. 定义 j 指针,作用是在时刻 i = T,把 [T-D, T) 之前的帖子都置为无效。具体的做法见第 20 行:用 j 遍历 [T-D, T) 之前的帖子,每遍历一个帖子,就把它的点赞数减一。注意这里的最关键之处:j 是跟随 i 的,而不是重新循环。

代码如下

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

大数据201 ly

### 取法在蓝桥杯竞赛中的应用 #### 什么是取法取法是一种高效的算法技巧,通常用于解决涉及连续子数组或字符串的问题。它的核心思想是从头到尾遍历序列的同时动态调整区间的大小和位置,从而减少不必要的计算。这种算法的时间复杂度通常是 \(O(n)\)[^3]。 #### 蓝桥杯中取法应用场景 在蓝桥杯竞赛中,取法常被用来解决以下两类问题: 1. **求最长不重复子串** 这类问题是给定一个字符串,要求找出其中最长的不含重复字符的子串长度。可以通过维护一个滑动窗口来记录当前无重复字符的最大范围,并不断更新最大值。 2. **模拟优先队列求区间最值** 此类问题可能涉及到在一个固定范围内寻找满足某些条件的最小或最大值。通过合理设置窗口边界并逐步移动,可以在较短时间内完成任务。 #### 示例题目解析:求最长不重复子串 假设我们有如下输入字符串 `s="abcabcbb"` ,目标是找到该字符串中最长的无重复字符子串及其长度。 ##### 解题思路 - 初始化两个指针分别表示当前考察区域的起始端与终止端; - 使用哈希集合存储已经访问过的字符以便快速判断是否有重复项; - 当遇到新字符未存在于集合内时将其加入并将右侧指针向前推进一位;如果发现存在冲突,则移除左侧第一个元素直至恢复唯一性再继续扩展右边界直到结束整个过程为止。 以下是基于上述描述的具体实现代码示例: ```python def length_of_longest_substring(s: str) -> int: char_set = set() max_len = 0 left = 0 for right, char in enumerate(s): while char in char_set: char_set.remove(s[left]) left += 1 char_set.add(char) max_len = max(max_len, right - left + 1) return max_len # 测试函数 if __name__ == "__main__": test_str = "abcabcbb" result = length_of_longest_substring(test_str) print(f"The longest substring without repeating characters is {result} long.") ``` 此段程序利用了双指针技术和辅助数据结构实现了高效查找功能。 #### 总结 通过对典型实例的学习可以看出,在面对需要频繁操作局部片段或者统计某特定条件下数值分布情况等问题时,采用取法则能够显著提升效率。它不仅限于字符串处理领域,在其他诸如数组分割、资源分配等方面同样具有广泛适用性和良好表现效果。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值