本节主要学习双指针、位运算、离散化、区间合并等知识。
双指针算法
常见类型:指向两个不同序列 或者 指向同一个系列的不同位置。

通用模版
for(i = 0, j =0; i < n; i++){
    while(j<i && check(i,j)) j++;
    //每道题目的具体逻辑
}
核心思想:将下面的朴素算法优化到 $O(n)$ 。
for(int i = 0; i < n; i++){
    for (int j = 0; j < n; j ++){
    }
}
举例:一串字符串里面单词用空格分隔,用双指针把每个单词都弄出来。
举例2:给定整数序列,求最长无重复数字的连续子序列长度。
做法:用指针i枚举右端点,指针j维护最远左端点,用哈希表记录区间内每个元素的出现次数,总共循环次数2n,时间复杂度降低到了 $O(n)$ 。
for (int i = 0, j = 0; i < n; i++){
    while(j < i && check(i,j)){
        pop(arr[j]);
        j++;
    }
    res = max(res,i-j+1);
}
常用位运算
- 求n的二进制表示中第k位是几(记个位是0位)
 
   n >> k & 1
作用:输出n的二进制表示。
- 返回x的最后一位1,如101000->1000
 
   x & -x = x & (~x + 1)

作用:求n的二进制表达中的1的个数。
离散化
整数、有序的离散化。比如一个数组值域为$(0,10^9)$,个数为$10^5$。此时如果想把数组值转化为下标使用,此时需要进行映射,这个过程称作离散化。
离散化可能用两个问题:1. a[]中可能有重复元素,需要去重;2.如何算出a[i]离散化后的值是多少。
解决办法:
先把所有值用sort排序,再用arr.erase(unique(arr.begin(),arr.end()),arr.end())去除重复元素,再使用二分查找查出来对应的值。
//手动实现unique函数
vector<int>::iterator unique(vector<int> &a){
    int j = 0;
    for(int i = 0; i < a.size(); i ++){
        if(!i || a[i] != a[i-1]) a[j++] = a[i];
    }
    return a.bigin() + j;
}
区间合并
给定n个区间 $[l_i, r_i]$ ,要求合并所有有交集的区间。
步骤:
- 按区间左端点排序。
 - 按左端点从小到大顺序遍历,记录一个当前的区间,扫描接下来的区间是否与当前区间有交集,若有交集则合并在一起并继续扫描,如果没有交集就把当前区间更新为这个区间。
 
void merge(vector<PII> &segs){
    vector<PII> res;
    sort(segs.begin(), segs.end());
    int st = -2e9, ed = -2e9;
    for(auto seg: segs){
        if(ed < seg.first){
            if (st != -2e9) res.push_back({st,ed});
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed,seg.second);
    }
    if(st != -2e9) res.push_back({st,ed});
    segs = res;
}
来自北京