目录
1.引入双指针
2.使用场景
3.例题引入
1.引入双指针
当我们需要维护某个区间性质的或者是求满足某些性质的区间的长度时,对于一个区间是由左右端点的,我们有简单的枚举左右端点的O()的时间的做法,当时在大多数题目中是不可行的,由此我们考虑是否具有某些性质使用双指针优化时间复杂度。
双指针简单而言就是有两个指针一前一后的移动着,同时两个指针都是单调的,所以在时间复杂度上是可以做到o()
2.使用场景
依据题目意思我们可以发现要求满足某些性质的区间中,当我们移动右指针的时候,如果在满足性质的同时左指针只会单调的移动,同时维护区间性质的话就可以使用双指针
3.例题引入
1.直接考察双指针 : 最长连续不重复子序列
给定一个长度为 n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含整数 n。
第二行包含 n个整数(均在 0∼100000 范围内),表示整数序列。
输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1≤n≤105
我们简单分析题意,是要求我们求满足某个性质的最长的区间,性质是不出现重复的数
我们可以发现对于判断数的重复可以使用一个桶来记录(本题数据范围小可使用数组)
接着分析问题可以得到一个基本结论,对于以i结尾的区间如果最长为对于以i+1结尾的区间
是那么一定是小于等于的(因为如果小区间都不满足了大区间一定是不满足的)由此可以知道每个的最大区间一定是不重复叠加的 也就是说左端点是递增的
其中核心就是小区间不满足,大区间一定不满足由此可以得出指针一定是单调移动的
那么我们考虑用一个指针来维护左端点,然后用桶来判断即可(实际上是以i结尾的最长不重复连续区间的长度)所以是双指针来解决这个题目
void solve(){
cin>>n;
vector<int> cnt(N);
vector<int> a(n+1);
int ans = 0;
for(int i=1,j=1;i<=n;i++){// 左指针为j ,右指针为i
cin>>a[i];
cnt[a[i]]++;
while(cnt[a[i]]>1){// 当前区间不满足性质
cnt[a[j]]--;
j++;// 开始移动左指针
}
ans = max(ans,i-j+1);
}
cout << ans << endl;
return ;
}
由此可以得到我们的基本模板
for(int i=1,j=1;i<=n;i++){
add(i)++; // add函数表示题目带来的性质
while(!check()){ // check()函数表示题目的性质
add(j)--;
j++;
}
}
2.双指针和前缀和的运用
对第一题进行变形变化为,求有满足恰好有三种不同数的区间的数量
此时我们发现如果是在上一题的基础上我们移动做指针是无法维护区间的的性质的,因为我们无法从一个满足要求的区间中得到这个区间俺的全部贡献所以直接双指针是不可行的,任意选择区间中的点得到的结果就是题目要求的<=要求的区间数量,对此我们对题目要求做一个简单的小处理
恰好等于 x == 小于等于x - 小于等于(x-1)这个时候我们对于这个题就可以用第一种方法解决
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
auto cal = [&](int x) -> ll {
map<int, int> m;
ll ans = 0;
for (int i = 1, l = 1, c = 0; i <= n; i++) {
if (++m[a[i]] == 1) c++;
while (c > x) {
if (--m[a[l++]] == 0) c--;
}
ans += i - l + 1;
}
return ans;
};
cout << cal(3) - cal(2) << endl;
}