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


    前言

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

    一、练习题目

            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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 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)的空间复杂度了。来一张对比图:
      在这里插入图片描述
  • 相关阅读:
    【元宇宙欧米说】对话NFT平台丨Maze NFT 协议
    Mysql的JDBC增删改查
    JavaScript基础——练习巩固题目(1)
    【UE5 C++ 學習日志】01. UEnhancedInput
    TensorRT基础笔记
    视频号视频下载教程,为视频博主提供的PC电脑版下载方法
    计算机网络知识之URL、IP、子网掩码、端口号
    [RCTF2015]EasySQL 二次注入 regexp指定字段 reverse逆序输出
    制作一个企业网站——html华为官网购物商城项目的设计与实现
    软件工程导论——第三章——需求分析
  • 原文地址:https://blog.csdn.net/weixin_42396991/article/details/125612672