题目描述如下:
双指针的核心思想:当要处理两层循环时,找到i与j的某种条件,使答案不用经过两次循环(O(n*2))而是经过i的一轮循环,通过i和j的关系确定j,将时间优化到O(n)
具体举例就如上题所示,寻找最长连续不重复子序列,我们当然可以通过暴力两层循环模拟题目描述的到答案,但数据范围1e5,当两层循环后1e10的计算量是大于我们计算机1e8每秒的运算次数,会使有些数据超时,此时我们考虑优化算法
暴力代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],cnt[N];//cnt数组用于记录每个元素出现的次数
int n,res;
//检查从i到j元素a[j]是否出现过,
bool check(int i,int j){
for(int k=i;k<j;k++){
if(a[k]==a[j]) return false;
}
return true;
}
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
while(check(i,j)==false){ //只要i到j中还有重复元素,让i右移动
i++;
}
res=max(res,j-i+1);
}
}
cout<<res;
}
优化算法的方法常常有二分,前缀和,双指针等小技巧,这里要求区间而非找数(临界),故与二分无关,求区间长度而非区间内与值相关的问题,前缀和大概率也用不上,再寻找区间(有两个变量)的问题上,我们可以考虑双指针
本题的思想有点类似哈希表,用空间换取时间,我们可以发现在暴力中,在考虑新加入的a[j]在区间[i,j]是否出现过时,没必要一遍遍跑一遍从i到j,我们可以通过一个计数的数组(学过桶排序的可能更好理解),只要数组中某一个数出现的次数(被计数的次数)大于1时,这时该区间就不满足要求,我们让左端点不断向右移动,寻找下一个(无重复元素的)区间,我们通过这个计数的数组,实现了i和j关系的连接
ac代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],cnt[N];//cnt数组用于记录每个元素出现的次数
int n;
int main(){
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
int mx=0;
for(int i=0,j=0;j<n;j++){
cnt[a[j]]++;
while(cnt[a[j]]>1){
cnt[a[i]]--;//i向右移,记录a[i]数组中a[i]的个数减一
i++;//i 右移
}
mx=max(mx,j-i+1);
}
cout<<mx;
}