【算法】——双指针

152 阅读1分钟

双指针

  • 常见问题分类:
  1. 对于一个序列,用两个指针维护一段区间
  2. 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

基本模版

for (int i = 0, j = 0; i < n; i ++ )
{
    while (j < i && check(i, j)) j ++ ;

    // 填充具体问题的逻辑
}

求最大不重复子序列

AcWing 799. 最长连续不重复子序列

开一个cnt数组,计数排序 两个指针i,j从头开始,cnt[a[i]]++ cnt如果是2了,就开始j++,直到cnt恢复到1 res更新,max(i-j+1,res)

#include <algorithm>
#include <cstdio>
using namespace std;

const int N = 1e5 + 10;
int n;
int a[N];
int cnt[N];
int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    int res = 0;
    for (int i = 0, j = 0; i < n; i++) {
        cnt[a[i]]++;
        while (i > j && cnt[a[i]] > 1)
            cnt[a[j++]]--;
        res = max(res, i - j + 1);
    }
    printf("%d", res);
    return 0;
}

两个序列a,b,求a[i]+b[j] == k的序列对(i,j)

AcWing 800. 数组元素的目标和

对于两个序列,如果循环两遍暴力o(n^2)

两个指针 i:n-1,j:0; 注意要留意i,j不为负

#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int n, m, k;
int main()
{
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    for (int i = 0; i < m; i++)
        scanf("%d", &b[i]);
    
    for (int i = n - 1, j = 0; i >= 0 && j < m; j++) {
        while (i >= 0 && a[i] + b[j] > k)
            i--;
        if (i>=0&&a[i] + b[j] == k)
            printf("%d %d\n", i, j);
    }
    return 0;
}

判断a是b的子序列

AcWing 2816. 判断子序列

两个指针i,j 如果a[i] == b[j],i++ j一直往后移动

#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int n, m;
int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    for (int i = 0; i < m; i++)
        scanf("%d", &b[i]);
    int i = 0,j = 0;
    while(i<n&&j<m){
        if(a[i] == b[j])    i++;
        j++;
    }
    if (i == n)
        puts("Yes");
    else
        puts("No");

    return 0;
}