LeetCode第195场周赛题解
题目来源:LeetCode官网
本文主要内容为 Acwing y总视频讲解的听课笔记及个人理解
1.LeetCode1496. 判断路径是否相交
题目描述
给你一个字符串 path,其中 path[i] 的值可以是 ‘N’、‘S’、‘E’ 或者 ‘W’,分别表示向北、向南、向东、向西移动一个单位。
机器人从二维平面上的原点 (0, 0) 处开始出发,按 path 所指示的路径行走。
如果路径在任何位置上出现相交的情况,也就是走到之前已经走过的位置,请返回 True ;否则,返回 False 。
示例 1:
输入:path = “NES”
输出:false
解释:该路径没有在任何位置相交。
示例 2:
输入:path = “NESWW”
输出:true
解释:该路径经过原点两次。
提示:
- 1 <= path.length <= 10^4
- path 仅由 {‘N’, ‘S’, ‘E’, 'W} 中的字符组成
分析
- 算法: 模拟 + 集合
- 时间复杂度: o(nlogn)
- 基本思路:
- 集合存储经过路径的下标,若当前下标已经在集合中,则路径相交
- 模拟遍历整个路径
代码
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)
- 基本思路:
- 因为是看配对两数相加是否是k的倍数,所以可以先将所有数模上k,统计相应余数的个数,存放到cnt[]
- 所有余数为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)
- 基本思路:
- 先将数组从小到大排序
- 将所有序列按照最小值分类,选取最右边位置j满足a[i]+a[j] <= target,因为a[i]总是要被计数,a[i+1]~a[j]可选可不选,所以最小值为i的序列的个数为2^(j - i)
- 选取j时因为数列单调,所以可以用一个指针来实现(也可以二分,不过更费时),i往后走,j只能不动或者往前走
- 重复元素枚举结果重复可以通过人为规定重复元素序列,类比于递归枚举的去重方法
代码
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)
- 基本思路:
- 可以人为定序xi大于xj,去掉绝对值
- yi + yj + xi - xj 等价于 (yj - xj) + (yi + xi)
- 固定yi + xi,用所有满足条件(xi - xj <= k)的(yj - xj)的最大值来更新答案
- 利用单调递减序列来实现,类似长度为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;
}
};