《七月集训》(第五天)——双指针

前言

        欢迎大家积极在评论区留言发表自己的看法,知无不言,言无不尽,养成每天刷题的习惯,也可以自己发布优质的解题报告,供社区一同鉴赏,吸引一波自己的核心粉丝。
        今天是七月集训第五天:双指针🔥🔥🔥
在这里插入图片描述

一、练习题目

        541. 反转字符串 II
        392. 判断子序列
        面试题 16.24. 数对和
        696. 计数二进制子串

二、算法思路

  • 1、541. 反转字符串 II:🔥这题之前做过,分阶段去翻转就好了,到2k个了如果剩余字符不足k就全部翻转,如果满足大于k小于2k的就翻转前k个即可。
  • 2、392. 判断子序列:🔥双指针。
  • 3、面试题 16.24. 数对和:🔥排序后,利用双指针计算两数之和。
  • 4、696. 计数二进制子串:🔥🔥🔥这题挂着easy的标签,实则想明白是需要花点时间的,想明白了就会发现这是个有意思的问题。

三、源码剖析

// 541. 反转字符串 II
class Solution {
public:
    string reverseStr(string s, int k) {
        int n = s.length();
        for (int i = 0; i < n; i += 2 * k)
        {
            reverse(s.begin() + i, s.begin() + min(i + k, n));
        }
        return s;
    }
};
  • 1、看思路详解。
// 392. 判断子序列
class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n = s.size(), m = t.size(), i = 0, j = 0;
        while (i < n && j < m) {
            if (s[i] == t[j]) //(1)
                i++;
            j++;
        }
        return i == n; //(2)
    }
};
  • 1、要找s是不是t的子序列,如果找到了i和j都要往后移;否则的话只移动j继续找;
  • 2、最后看下是不是和s的长度相等。
// 面试题 16.24. 数对和
class Solution {
public:
    vector<vector<int>> pairSums(vector<int>& nums, int target) {
        vector<vector<int>> ans;
        sort(nums.begin(), nums.end()); //(1)
        int i = 0, j = nums.size() - 1;
        while(i < j) {
            int val = nums[i] + nums[j];
            if(val > target) {
                --j;
            } else if(val < target) {
                ++i;
            } else {
                ans.push_back({nums[i], nums[j]}); //(2)
                ++i;
                --j;
            }
        }
        return ans;
    }
};
  • 1、排序之后利用双指针去求两数之和;
  • 2、这里是有大于小于和等于三种情况考虑,大于就把j减小,小于把i增大,如果等于的话说明找到了一个满足条件的放入二维数组中去,再移动双指针。
// 696. 计数二进制子串
class Solution {
public:
    int countBinarySubstrings(string s) {
        vector<int> counts;
        s += '-'; //(1)
        int n = s.length();
        int count = 1;
        for(int i = 1; i < n; ++i) {
            if(s[i] == s[i - 1]) {
                count++;
            } else {
                counts.push_back(count);
                count = 1;
            }
        } //(2)
        int ans = 0;
        for(int i = 1; i < counts.size(); ++i) {
            ans += min(counts[i], counts[i - 1]);
        } //(3)
        return ans;
    }
};

// 双指针
class Solution {
public:
    int countBinarySubstrings(string s) {
        s += '-';
        int n = s.length();
        int count = 1, prev = 0;
        int ans = 0;
        for(int i = 1; i < n; ++i) {
            if(s[i] == s[i - 1]) {
                count++;
            } else {
                ans += min(count, prev);
                prev = count;
                count = 1;
            }
        }
        return ans;
    } //(5)
};
  • 1、在字符串末尾加一个东西,不然我们去比较最后一个字符是不会被记录的,就像分割字符串的时候,我们也需要在最后一个末尾加入不相干的字符辅助;
  • 2、这一步是在做一个统计并且拆分的事情,举一个例子:"00110011,这是题目的例子,这一步就是一次统计连续的0或者1的个数,放到counts数组中去,"00110011这个例子应该很容易看出,2个0、2个1、2个0再接2个1,counts{2,2,2,2}
  • 3、这时候去遍历counts,先看counts[0]counts[1],得到的是2个0和2个1,即0011,这里的贡献其实就是2种情况010011,即min(counts[0], counts[1]),那么接下去遍历1100贡献也是2,得到的是101100……所以推出每一个位置对答案的贡献是min(counts[i], counts[i - 1])
  • 4、我上面这个例子可能也比较特殊连续的个数都是2,那再不明白我们再举例子好了,比如00111011,这样counts{2,3,1,2}。接下去逐个去遍历counts,当2个0和3个1,最多贡献0100112种;当3个1和1个0,最多贡献101种;当1个0和2个1,最多贡献011种,最终就是4种情况。
  • 5、上面用数组需要O(n)的空间的空间复杂度,但是我们其实考虑的是当前位置i和i - 1位置之间的关系,那我们用一个prev指针记录前一个位置的值就只需要O(1)的空间复杂度了。来一张对比图:
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值