尺取法概念:双指针,算法竞赛中一个常用的优化技巧,操作简单、容易编程。
为什么尺取法能用来优化?
一把两种循环转化为一重循环,从而把复杂度从O(n2)提高到O(n)。
两种写法: for、while
for (int i = 0,j = n - l; i < j; i++, j--) //i从头到尾,j从尾到头
{......}
int i = 0,j = n - 1;
while (i< j) //i和j在中间相遇。这样做还能防止i、j越界//满足题意的操作
{
i++; //i从头扫到尾
j--; //j从尾扫到头
}
例题:回文判定。
给定一个长度为n的字符串S,判断是否回文。
#include <bits/stdc++.h>
using namespace std;
int main(){
string s;
cin >> s; //读字符串
bool ans = true;
int i = 0, j = s.size() - l; //双指针
//反向扫描
while(i < j)
{
if(s[i] != s[j]){ ans = false; break; } //不是回文
i++;j--; //移动双指针
}
if(ans)cout << "Y"<< endl ;
else cout<<"N"<< endl;
return 0;
}
例题二:寻找区间和
题目描述:给定一个长度为n的数组a[]和一个数s,在这个数组中找一个区间,使得这个区间之和等于s。输出区间的起点和终点位置。
解析:
- 初始值i=0、j=0,即开始都指向第一个元素a[0] ,定义sum是区间[i,j]的和,初始值sum = a[0]。
- 如果sum等于s,输出一个解。继续,把sum减掉元素a[i],并把i往后移动一位。
- 如果sum大于s,让sum减掉元素a[i],开把i往后移动一位。
- 4.如果sum小于s,把j往后挪一位,并把sum的值加上这个新元素。
void findSum(int *a,int n,int s)
{
int i = 0,j= 0;
int sum = a[0];
while(j < n){ //下面代码中保证 i<=j
if(sum >= s){
if(sum == s) printf("%d %d\n", i,j);
sum -= a[i];
i++;
if(i>j) {sum = a[i]; j++;} //防止i超过j
}
if(sum< s){ j++; sum += a[j];}
}
}
例题三:锻造兵器
题目描述:有n块锻造石,第i块锻造石的属性值为ai。从这n块锻造石中任取两块来锻造兵器。
只有当两块锻造石的属性值的差值等于C,兵器才能锻造成功。请算算,有多少种选取锻造石的方案。
输入样例: 6 3
8 4 5 7 7 4
输出样例: 5
分析:任取两数,差值为C。样例中C=3,有5种方案:
8 5
4 7
4 7
7 4
7 4
#include <bits/stdc++.h>using namespace std;
const int N = 2e5+ 5;
int a[N];
int main (){
int n , c; cin >> n >> c;
for(int i= l ; i <=n ; i ++) cin >> a[i];
sort(a + l , a + l + n); //排序
long long ans = 0;
/**
三指针:
i是主指针,从头到尾遍历n个数;
j、k是辅助指针,用于查找数字相同的区间[j,k]。
**/
for(int i=1,j=1,k=l ; i<= n ; i ++){
while(j <=n && a[j] - a[i]〈c ) j++; //用j、k查找数字相同的区间
while(k <=n && a[k] - a[i]<= c) k++; //区间[j, k]内所有数字相同
if(a[j]-a[i]==c && a[k-1]-a[i]==c && k-1>=1)
ans += k - j; //统计数对
}
cout<<ans;
return 0;
}