LeetCode第195场周赛题解

LeetCode第195场周赛题解

题目来源:LeetCode官网
本文主要内容为 Acwing y总视频讲解的听课笔记及个人理解


1.LeetCode1496. 判断路径是否相交

题目描述

给你一个字符串 path,其中 path[i] 的值可以是 ‘N’、‘S’、‘E’ 或者 ‘W’,分别表示向北、向南、向东、向西移动一个单位。
机器人从二维平面上的原点 (0, 0) 处开始出发,按 path 所指示的路径行走。
如果路径在任何位置上出现相交的情况,也就是走到之前已经走过的位置,请返回 True ;否则,返回 False 。

示例 1:
示例1输入:path = “NES”
输出:false
解释:该路径没有在任何位置相交。
示例 2:
示例2
输入:path = “NESWW”
输出:true
解释:该路径经过原点两次。

提示:

  • 1 <= path.length <= 10^4
  • path 仅由 {‘N’, ‘S’, ‘E’, 'W} 中的字符组成

分析

  • 算法: 模拟 + 集合
  • 时间复杂度: o(nlogn)
  • 基本思路:
  1. 集合存储经过路径的下标,若当前下标已经在集合中,则路径相交
  2. 模拟遍历整个路径

代码

class Solution {
public:
    bool isPathCrossing(string path) {
        int n = path.size();
        unordered_set<string>hash;
        int x = 0, y = 0;
        hash.insert("0 0");
        for(int i = 0; i < n; i++)
        {
            if(path[i] == 'N')x--;
            else if(path[i] == 'S')x++;
            else if(path[i] == 'W')y--;
            else y++;
            string t = to_string(x) + " " + to_string(y);
            if(hash.count(t))return true;
            else hash.insert(t);
        }
        return false;
    }
};

2. LeetCode 1497. 检查数组对是否可以被 k 整除

题目描述

检查数组对是否可以被 k 整除给你一个整数数组 arr 和一个整数 k ,其中数组长度是偶数,值为 n 。现在需要把数组恰好分成 n / 2 对,以使每对数字的和都能够被 k 整除。如果存在这样的分法,请返回 True ;否则,返回 False 。

示例 1:
输入:arr = [1,2,3,4,5,10,6,7,8,9], k = 5
输出:true
解释:划分后的数字对为 (1,9),(2,8),(3,7),(4,6) 以及 (5,10) 。
示例 2:
输入:arr = [1,2,3,4,5,6], k = 7
输出:true
解释:划分后的数字对为 (1,6),(2,5) 以及 (3,4) 。
示例 3:
输入:arr = [1,2,3,4,5,6], k = 10
输出:false
解释:无法在将数组中的数字分为三对的同时满足每对数字和能够被 10 整除的条件。
示例 4:
输入:arr = [-10,10], k = 2
输出:true
示例 5:
输入:arr = [-1,1,-2,2,-3,3,-4,4], k = 3
输出:true

提示:

  • arr.length == n
  • 1 <= n <= 105
  • n 为偶数
  • -109 <= arr[i] <= 109
  • 1 <= k <= 105

分析

  • 算法: 模拟 + 哈希
  • 时间复杂度: o(n)
  • 基本思路:
  1. 因为是看配对两数相加是否是k的倍数,所以可以先将所有数模上k,统计相应余数的个数,存放到cnt[]
  2. 所有余数为0的数要和本身配对,即要求cnt[0]为偶数,余数大于0的数i要和k-i配对,即要求cnt[i] = cnt[k - i]

代码

class Solution {
public:
    bool canArrange(vector<int>& arr, int k) {
        unordered_map<int, int>cnt;
        for(int i = 0; i < arr.size(); i++)cnt[(arr[i] % k + k) % k]++;
        if(cnt[0] & 1)return false;
        for(int i = 1; i < k; i++)
            while(cnt[i])
            {
                cnt[i]--;
                if(cnt[k - i] <= 0)return false;
                cnt[k - i]--;
            }
        return true;
    }
}; 

3.LeetCode 1498. 满足条件的子序列数目

题目描述

给你一个整数数组 nums 和一个整数 target 。
请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。
由于答案可能很大,请将结果对 10^9 + 7 取余后返回。

示例 1:
输入:nums = [3,5,6,7], target = 9
输出:4
解释:有 4 个子序列满足该条件。
[3] -> 最小元素 + 最大元素 <= target (3 + 3 <= 9)
[3,5] -> (3 + 5 <= 9)
[3,5,6] -> (3 + 6 <= 9)
[3,6] -> (3 + 6 <= 9)
示例 2:
输入:nums = [3,3,6,8], target = 10
输出:6
解释:有 6 个子序列满足该条件。(nums 中可以有重复数字)
[3] , [3] , [3,3], [3,6] , [3,6] , [3,3,6]
示例 3:
输入:nums = [2,3,3,4,6,7], target = 12
输出:61
解释:共有 63 个非空子序列,其中 2 个不满足条件([6,7], [7])
有效序列总数为(63 - 2 = 61)
示例 4:
输入:nums = [5,2,4,1,7,6,8], target = 16
输出:127
解释:所有非空子序列都满足条件 (2^7 - 1) = 127

提示:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^6
  • 1 <= target <= 10^6

分析

  • 算法: 双指针
  • 时间复杂度: o(nlogn)
  • 基本思路:
  1. 先将数组从小到大排序
  2. 将所有序列按照最小值分类,选取最右边位置j满足a[i]+a[j] <= target,因为a[i]总是要被计数,a[i+1]~a[j]可选可不选,所以最小值为i的序列的个数为2^(j - i)
  3. 选取j时因为数列单调,所以可以用一个指针来实现(也可以二分,不过更费时),i往后走,j只能不动或者往前走
  4. 重复元素枚举结果重复可以通过人为规定重复元素序列,类比于递归枚举的去重方法

代码

class Solution {
public:
    int numSubseq(vector<int>& nums, int target) {
        int n = nums.size(), mod = 1e9 + 7;
        vector<int>p(n);
        p[0] = 1;
        for(int i = 1; i < n; i++)p[i] = (p[i - 1]  * 2) % mod;
        int res = 0;
        sort(nums.begin(), nums.end());
        for(int i = 0, j = n - 1; i <= j; i++)
        {
            while(i <= j && nums[i] + nums[j] > target)j--;
            if(i <= j && nums[i] + nums[j] <= target)
            {
                res = (res + p[j - i]) % mod;
            }
        }
        return res;
    }
}; 

4. LeetCode 1499. 满足不等式的最大值

题目描述

给你一个数组 points 和一个整数 k 。数组中每个元素都表示二维平面上的点的坐标,并按照横坐标 x 的值从小到大排序。也就是说 points[i] = [xi, yi] ,并且在 1 <= i < j <= points.length 的前提下, xi < xj 总成立。
请你找出 yi + yj + |xi - xj| 的 最大值,其中 |xi - xj| <= k 且 1 <= i < j <= points.length。
题目测试数据保证至少存在一对能够满足 |xi - xj| <= k 的点。

示例 1:
输入:points = [[1,3],[2,0],[5,10],[6,-10]], k = 1
输出:4
解释:前两个点满足 |xi - xj| <= 1 ,带入方程计算,则得到值 3 + 0 + |1 - 2| = 4 。第三个和第四个点也满足条件,得到值 10 + -10 + |5 - 6| = 1 。
没有其他满足条件的点,所以返回 4 和 1 中最大的那个。
示例 2:
输入:points = [[0,0],[3,0],[9,2]], k = 3
输出:3
解释:只有前两个点满足 |xi - xj| <= 3 ,带入方程后得到值 0 + 0 + |0 - 3| = 3 。

提示:

  • 2 <= points.length <= 10^5
  • points[i].length == 2
  • -10^8 <= points[i][0], points[i][1] <= 10^8
  • 0 <= k <= 2 * 10^8
  • 对于所有的1 <= i < j <= points.length ,points[i][0] < points[j][0] 都成立。也就是说,xi 是严格递增的。

分析

  • 算法: 单调队列/滑动窗口
  • 时间复杂度: o(n)
  • 基本思路:
  1. 可以人为定序xi大于xj,去掉绝对值
  2. yi + yj + xi - xj 等价于 (yj - xj) + (yi + xi)
  3. 固定yi + xi,用所有满足条件(xi - xj <= k)的(yj - xj)的最大值来更新答案
  4. 利用单调递减序列来实现,类似长度为k滑动窗口模型

代码

class Solution {
public:
    int findMaxValueOfEquation(vector<vector<int>>& points, int k) {
        int res = INT_MIN;
        deque<int>q;
        for(int i = 0; i < points.size(); i++)
        {
            int x = points[i][0], y = points[i][1];
            while(q.size() && x - points[q.front()][0] > k)q.pop_front();
            if(q.size())
            {
                int t = q.front();
                res = max(res, points[t][1] - points[t][0] + x + y);
            }
            while(q.size() && y - x >= points[q.back()][1] - points[q.back()][0])q.pop_back();
            q.push_back(i);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值