尺取法详解

尺取法概念:双指针,算法竞赛中一个常用的优化技巧,操作简单、容易编程。

为什么尺取法能用来优化?

一把两种循环转化为一重循环,从而把复杂度从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。输出区间的起点和终点位置。

解析:

  1. 初始值i=0、j=0,即开始都指向第一个元素a[0] ,定义sum是区间[i,j]的和,初始值sum = a[0]。
  2. 如果sum等于s,输出一个解。继续,把sum减掉元素a[i],并把i往后移动一位。
  3. 如果sum大于s,让sum减掉元素a[i],开把i往后移动一位。
  4. 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;

}

  • 9
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个经典的字符串问题,可以使用双指针尺取法来解决。具体的实现过程如下: 1. 首先定义一个哈希表 `charSet`,用来记录当前子串中出现的所有字符。 2. 定义两个指针 `left` 和 `right`,初始时都指向序列的第一个位置。 3. 定义一个变量 `minLen`,用来记录满足条件的最短子串的长度。初始值可以设置为整个序列的长度。 4. 不断移动右指针 `right`,将对应的字符添加到 `charSet` 中,直到出现了所有的 26 个英文字母。 5. 移动左指针 `left`,将对应的字符从 `charSet` 中删除,直到出现了某个字符删除后 `charSet` 中不再包含所有的 26 个英文字母。 6. 计算当前子串的长度,如果比 `minLen` 更短,则更新 `minLen` 的值。 7. 重复步骤 4 到 6,直到右指针到达序列的末尾。 8. 返回 `minLen` 的值,即为满足条件的最短子串的长度。 以下是示例代码实现: ```c++ #include <iostream> #include <unordered_set> #include <string> using namespace std; int findShortestSubstring(string s) { int n = s.size(); unordered_set<char> charSet; int left = 0, right = 0; int minLen = n; while (right < n) { charSet.insert(s[right]); while (charSet.size() == 26) { minLen = min(minLen, right - left + 1); charSet.erase(s[left]); left++; } right++; } return minLen; } int main() { string s = "abcdefghijklmnopqrstuvwxyz"; int len = findShortestSubstring(s); cout << len << endl; // 输出 26 return 0; } ``` 注意,由于这个问题中只包含小写字母,所以哈希表可以用 `unordered_set<char>` 来实现。如果有其他字符的情况,可以考虑使用哈希表来记录每个字符出现的次数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值