开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情
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:数组元素的目标和
暴力做法:
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;
}