双指针算法
核心思想
- 暴力枚举解题
- 观察是否有单调性,有则可以使用双指针降低时间复杂度
最长连续不重复子序列
核心思想 :
- 遍历数组a中的每一个元素a[i],计算每一个以a[j]开始a[i]结尾的连续子序列的长度,并将长度i - j + 1 与较大的 res 比较并更新(res = max(res, i - j + 1));
- 对于每一个i,如何确定j的位置:由于[j, i - 1]是前一步得到的最长连续不重复子序列,所以如果[j, i]中有重复元素,一定是a[i],因此右移j直到a[i]不重复为止([j, i - 1]已经是前一步的最优解,由于i 和 j 都具有单调性,j不可能左移,所以出现重复的数字只能是a[i]);
- 用数组s记录子序列a[ j ] ~~~ a[ i ] 中各元素出现次数。若s[ a[ i ] ] > 1了则代表出现了重复的数字 a[ i ] ,通过 j ++ 来移动子序列的左边界,直到将出现的第一个数 a[ i ] 排除出去。{ while (s[a[i]] > 1) }时执行 { s[ a[i] ] - - } { j ++ } //先将丢弃的数字出现的次数减去,再把 j 右移;
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N],s[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 ++)
{
s[a[i]] ++;
while(s[a[i]] > 1) -- s[a[j ++]]; //先减去次数后右移
res = max(res, i - j + 1);
}
cout << res << endl;
return 0;
}
数组元素的目标和
题目 :
给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。
数组下标从 0 开始。
请你求出满足 A[i]+B[j]=x 的数对 (i,j)。
数据保证有唯一解。
解题 :
- 列出暴力枚举公式(两个for循环)。
- 两数组单调递增,使用双指针,降低时间复杂度。
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n,m,x;
int a[N], b[N];
int main()
{
scanf("%d%d%d",&n, &m, &x);
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 = 0, j = m - 1; i < n; i ++)
{
while(j >= 0 && a[i] + b[j] > x) j --;
if(j >= 0 && a[i] + b[j] == x)
{
cout << i << ' ' << j << endl;
break;
}
}
return 0;
}
判断子序列
题目:
给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。
请你判断 a 序列是否为 b 序列的子序列。
子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5} 的一个子序列。
#include<iostream>
using namespace std;
const int N = 100010;
int n, m;
int a[N],b[N];
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) printf("Yes");
else printf("No");
return 0;
}