最长连续不重复子序列&&数组元素的目标和

87 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情

image-20221030100358886

1)遍历数组a中的每一个元素a[i],对于每一个i位置,找到j位置使得双指针[j, i]维护的是以a[i]结尾的最长连续不重复子序列,这段区间的长度为`i - j + 1, 将这一长度与ans的较大者更新给ans

2)对于每一个i位置,如何确定j的位置: 由于`[j, i - 1]是前一步得到的最长连续不重复子序列,所以如果[j, i]中有重复元素,一定是这个新增加的a[i]元素,因此右移j位置,直到a[i]位置不重复为止, 由于[j, i - 1]已经是前一步的最优解,此时j只可能右移以剔除重复元素a[i],j不可能左移增加元素,因此,j具有“单调性”,可用双指针降低复杂度

3)为了方便可以用数组s记录当前子序列a[j ~ i]中每个元素出现次数

#include<iostream>
using namespace std;
const int N = 100010;
int n;
int a[N],s[N];
int main()
{
    cin >> n;
    for(int i = 0;i<n;i++) cin >> a[i];
    
    int ans = 0 ;
    //s数组:记录当前子序列a[j ~ i]中每个元素出现次数
    for(int i = 0,j = 0;i<n;i++)  //j为左指针,i为右指针
    {
        s[a[i]]++;//记录a[i]在[j,i]区间出现的次数
        
        //当a[i][j,i]区间出现次数>1,说明有元素(a[i])重复出现了
        while(s[a[i]] > 1)
        {
            //左指针j开始移动,不断缩小区间
            s[a[j]]--; //当前j位置元素移除==>当前a[j]元素在子序列a[j ~ i]中出现次数-1
            j++;//向后移动
        }
        //[j,i]就是当前以i位置元素结尾的最长连续不重复子序列
        ans = max(ans,i-j+1);
    }
    cout << ans<<endl;
    return 0;
}

例子3:数组元素的目标和

image-20221102213913609

暴力做法:

for(int i = 0;i<n;i++){
    for(int j = 0;j<m;j++)
        if(A[i] + B[j] == x)
            //输出答案
}

双指针做法:注意A和B数组是有序的, A数组从前往后遍历,B数组从后往前遍历 ,此时a[i]最小,b[j]最大

  • 当a[i] + b[j] > x的时候,j一直往前移动,b[j]的值一直在减小,直到a[i] + b[j] 的值第一次小于x时,j指针的左边的数与a[i]相加一定都小于x了,那么我们只能增大a[i]的值,i++

总计起来就是:如果a[i] + b[j] 大于x,就减小b[j],(相当于将j指针左移),否则就增大a[i],相当于i指针右移

#include<iostream>
using namespace std;
const int N = 100010;
int n,m,x;
int a[N],b[N];
​
int main()
{
    cin >> n>> m >> x ;
    for(int i = 0;i < n;i++) cin >> a[i];
    for(int i = 0;i < m;i++) cin >> b[i];
    int i = 0,j = m-1;
    while(i<n)
    {
        while(j>=0 &&a[i] + b[j] > x)       j--;
        if(a[i] + b[j] == x)
        {
            cout << i <<" " <<  j << endl; // 下标打印
            break;
        }
        i++;
    }
    return 0;
}