【算法300题】:双指针

双指针板块

925. 长按键入

leetcode链接

你的朋友正在使用键盘输入他的名字 name。偶尔,在键入字符 c 时,按键可能会被长按,而字符可能被输入 1 次或多次。

你将会检查键盘输入的字符 typed。如果它对应的可能是你的朋友的名字(其中一些字符可能被长按),那么就返回 True。

思路

这道题目只要是末尾的边界条件比较恶心一点

class Solution {
public:
    bool isLongPressedName(string name, string typed) 
    {
        int cur = 0;
        if(name[0] != typed[0]) return false;
        for(auto ch : name)
        {
            if(ch == typed[cur])
            {
                // 匹配成功
                ++cur;
            }
            else 
            {
                // 匹配失败
                // 可能上一个字符多键入了
                auto prev_chr = typed[cur - 1];
                while(cur < typed.size() && typed[cur] == prev_chr) cur++;

                if(cur < typed.size() && typed[cur] == ch) 
                {
                    cur++;
                }
                else return false; 
            }
            
        }
        // 找找还有没有词
        auto prev_chr = typed[cur - 1];
        while(cur < typed.size() && typed[cur] == prev_chr) cur++;

        if(cur < typed.size()) return false;
        return true;
    }
};

532. 数组中的 k-diff 数对

leetcode链接

给你一个整数数组 nums 和一个整数 k,请你在数组中找出 不同的 k-diff 数对,并返回不同的 k-diff 数对 的数目。

k-diff 数对定义为一个整数对 (nums[i], nums[j]) ,并满足下述全部条件:

0 <= i, j < nums.length
i != j
|nums[i] - nums[j]| == k
注意,|val| 表示 val 的绝对值。

示例 1:

输入:nums = [3, 1, 4, 1, 5], k = 2
输出:2
解释:数组中有两个 2-diff 数对, (1, 3) 和 (3, 5)。
尽管数组中有两个 1 ,但我们只应返回不同的数对的数量。
示例 2:

输入:nums = [1, 2, 3, 4, 5], k = 1
输出:4
解释:数组中有四个 1-diff 数对, (1, 2), (2, 3), (3, 4) 和 (4, 5) 。
示例 3:

输入:nums = [1, 3, 1, 5, 4], k = 0
输出:1
解释:数组中只有一个 0-diff 数对,(1, 1)

思路

第一版的思路就是直接查找, 但是k == 0单独讨论一下, 不然确实不好处理

class Solution {
public:
    int getPrevDiffNum(vector<int>& num,int pos)
    {
        int res = pos - 1;
        while(res >= 0 && num[res] == num[pos]) res--;

        return res;
        // -1
    }

    int getNextDiffNum(vector<int>& num,int pos)
    {
        int res = pos + 1;
        while(res < num.size() && num[res] == num[pos]) res++;

        return res;
    }
    int findPairs(vector<int>& nums, int k) 
    {
        // 暴力算法
        // 单独处理k == 0
       
        sort(nums.begin(), nums.end());
        if(k == 0)
        {
            int count = 0;
            for(int i = 0;i < nums.size();i = getNextDiffNum(nums,i))
                if(i + 1 < nums.size() && nums[i] == nums[i + 1]) count++;
            return count;
        }
        int n = nums.size();
        int count = 0;
        for(int i = 0;i < n;i = getNextDiffNum(nums,i))
        {
            auto prev_diff_pos = i;
            while(prev_diff_pos != -1)
            {
                if(nums[i] - nums[prev_diff_pos] >= k) break;

                prev_diff_pos = getPrevDiffNum(nums, prev_diff_pos);
            }
            if(prev_diff_pos != -1 && nums[i] - nums[prev_diff_pos] == k) 
            {
                count++;
            }

        }

        return count;
    }
};

思路

我们在查找的时候可以使用二分进行速度的优化。确实快了一些

class Solution {
public:
    int getPrevDiffNum(vector<int>& num,int pos)
    {
        int res = pos - 1;
        while(res >= 0 && num[res] == num[pos]) res--;

        return res;
        // -1
    }

    int getNextDiffNum(vector<int>& num,int pos)
    {
        int res = pos + 1;
        while(res < num.size() && num[res] == num[pos]) res++;

        return res;
    }
    int findPairs(vector<int>& nums, int k) 
    {
        // 暴力算法
        // 单独处理k == 0
       
        sort(nums.begin(), nums.end());
        if(k == 0)
        {
            int count = 0;
            for(int i = 0;i < nums.size();i = getNextDiffNum(nums,i))
                if(i + 1 < nums.size() && nums[i] == nums[i + 1]) count++;
            return count;
        }
        // 如果k != 0, 首先进行去重
        // 通过二分查找进行优化之前的逻辑
        int count = 0;
        for(int i = getNextDiffNum(nums,0);i < nums.size();i = getNextDiffNum(nums,i))
        {
            int left = 0, right = i - 1;
            while(left < right)
            {
                int mid = left + (right - left) / 2;
                if(nums[i] - nums[mid] > k) left = mid + 1;
                else right = mid;
            }
            if(nums[i] - nums[left] == k) count++;
        }
        return count;
    }
};

56. 合并区间

leetcode

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

class Cmp
{
public:
    // we must guarentee vector.size() == 2
    bool operator()(const vector<int>& l,const vector<int>& r)
    {
        return l[0] < r[0];
    }
};

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) 
    {
        vector<vector<int>> ret;
        std::sort(intervals.begin(),intervals.end(),Cmp());
        int prev_end = intervals[0][0];
        int prev_begin = intervals[0][0];
        intervals.push_back({INT32_MAX,INT32_MAX});
        for(auto& area : intervals)
        {
            int begin = area[0], end = area[1];
            if(prev_end < begin)
            {
                // 更新区间
                ret.push_back({prev_begin,prev_end});
                prev_begin = begin, prev_end = end;
            }
            else if(begin <= prev_end)
            {
                // 出现交叉
                // 合并
                prev_end = std::max(prev_end,end);
            }
        }    
        return ret;
    }
};

75. 颜色分类

leetcode

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]

class Solution {
public: 
    // 通过快排
    
    void sortColors(vector<int>& nums) {
        int left = -1, right = nums.size();
        int cur = 0;
        while(cur < right)
        {
            if(nums[cur] == 0) std::swap(nums[cur++], nums[++left]);
            else if(nums[cur] == 1) cur++;
            else std::swap(nums[cur], nums[--right]);
        }    

    }
};

80. 删除有序数组中的重复项 II

leetcode

给定一个二进制数组 nums , 计算其中最大连续 1 的个数。

示例 1:

输入:nums = [1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.
示例 2:

输入:nums = [1,0,1,1,0,1]
输出:2

提示:

1 <= nums.length <= 105
nums[i] 不是 0 就是 1.

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        // 很简单的双指针的算法
        // count表示当前位置前面需要删除的个数
        int symbol = INT32_MAX;
        int symbol_count = 0;
        int offset = 0;
        for(int i = 0;i < nums.size();i++)
        {
            if(nums[i] == symbol) 
            {
                symbol_count++;
                if(symbol_count > 2)
                    offset++;
                else
                {
                    nums[i - offset] = nums[i]; 
                }      
            }
            else 
            {
                symbol = nums[i];
                symbol_count = 1;
                nums[i - offset] = nums[i];
            }
        }

        auto res = nums.size() - offset;
        nums.resize(res);
        return res;
    }
};

485. 最大连续 1 的个数

leetcode

给定一个二进制数组 nums , 计算其中最大连续 1 的个数。

示例 1:

输入:nums = [1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.
示例 2:

输入:nums = [1,0,1,1,0,1]
输出:2

class Solution {
public:
    int findMaxConsecutiveOnes(vector<int>& nums) 
    {
        int res = 0;
        int count = 0;
        
        for(auto i : nums) 
        {
            if(i == 0)
            {
                count = 0;
            }
            else 
            {
                count++;
                res = std::max(res,count);
            }
        }    
        return res;
    }
};

524. 通过删除字母匹配到字典里最长单词

给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。

如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。

示例 1:

输入:s = “abpcplea”, dictionary = [“ale”,“apple”,“monkey”,“plea”]
输出:“apple”
示例 2:

输入:s = “abpcplea”, dictionary = [“a”,“b”,“c”]
输出:“a”

class Solution {
public:
    Solution() : _hash(26)
    {}
    void initHashTable(const std::string& str)
    {
        for(int i = 0;i < str.size();i++)
            _hash[str[i] - 'a'].push_back(i);
    }
    bool isFitChildString(const std::string& src)
    {
        int cur = -1; 
        for(auto ch : src)
        {
            // 判断ch是否在(cur, end]中存在, 若存在找到最近的一个
            // 通过二分查找进行优化
            auto& posSet = _hash[ch - 'a'];
            if(posSet.empty()) return false;
            else
            {
                int left = 0, right = posSet.size() - 1;
                while(left < right)
                {
                    int mid = left + (right - left) / 2;
                    if(posSet[mid] <= cur) left = mid + 1;
                    else right = mid;
                }

                if(posSet[left] <= cur) return false;
                else cur = posSet[left];
            }
            
        }

        return true;
    }
    // 这道题目的本质就是判断dictionary中是否存在s的子序列, 对于大量判断, 通过hash来解决
    string findLongestWord(string s, vector<string>& dictionary) 
    {
        initHashTable(s);
        string res;
        for(auto& dict : dictionary)
        {
            if(isFitChildString(dict))
            {
                if(dict.size() > res.size()) res = dict;
                else if(dict.size() == res.size() && dict < res) res = dict;
            }
        }
        return res; 
    }
// 通过hash + 二分进行优化

private:
    vector<vector<int>> _hash;
};

986. 区间列表的交集

leetcode

给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序 。

返回这 两个区间列表的交集 。

形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b 。

两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3] 。

示例 1:

输入:firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]]
输出:[[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]]
示例 2:

输入:firstList = [[1,3],[5,9]], secondList = []
输出:[]
示例 3:

输入:firstList = [], secondList = [[4,8],[10,12]]
输出:[]
示例 4:

输入:firstList = [[1,7]], secondList = [[3,10]]
输出:[[3,7]]

class Solution {
public:
    struct InterArea
    {
        InterArea(int left = -1,int right = -1) : 
            _left(left), _right(right)
        {}

        operator bool()
        {
            return _left != -1;
        } 
        int _left;
        int _right;
    };
    // 判断两个区间是否存在公共部分
    InterArea isHasInterSection(vector<int>& l, vector<int>& r)
    {
        if(l[0] > r[1] || l[1] < r[0]) return {-1, -1 };
        else return InterArea((int)std::max(l[0],r[0]), (int)std::min(l[1], r[1]));
    }
    vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList, vector<vector<int>>& secondList) 
    {
        vector<vector<int>> res;
        int start = 0;
        for(auto& List : secondList)
        {
            
            while(start < firstList.size() && firstList[start][1] < List[0]) start++;
            auto cur = start;
            // 计算区间
            while(cur < firstList.size() && firstList[cur][0] <= List[1])
            {

                auto inter = isHasInterSection(firstList[cur], List);
                res.push_back({inter._left, inter._right});
                cur++;
            }
        }
        return res;
    }
};

你可能感兴趣的:(【算法300题】:双指针)