周赛第195场20200628
1496. 判断路径是否相交
题目描述1
给你一个字符串 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}
中的字符组成
Solution1
- 用HashSet记录是否走到过,每次将走过的坐标存入,借助Pair<Integer, Integer>存坐标
class Solution {
public boolean isPathCrossing(String path) {
int n = path.length();
Set<Pair<Integer, Integer>> set = new HashSet<>();
int i = 0;
int j = 0;
Pair<Integer, Integer> pair = new Pair<>(i, j);
set.add(pair);
for(int k = 0; k < n; k++){
char c = path.charAt(k);
switch(c){
case 'N' : i--; break;
case 'S' : i++; break;
case 'W' : j--; break;
case 'E' : j++; break;
}
pair = new Pair<>(i, j);
if(set.contains(pair)){
return true;
}else{
set.add(pair);
}
}
return false;
}
}
- 自己实现哈希映射函数,比如令哈希函数
f(x, y) = x * 20001 + y
。因为因为 y的取值范围在[-10^4, 10^4]
内,共有20001
种可能性
class Solution {
public boolean isPathCrossing(String path) {
int n = path.length();
Set<Integer> set = new HashSet<>();
int i = 0;
int j = 0;
set.add(getHash(i, j));
for(int k = 0; k < n; k++){
char c = path.charAt(k);
switch(c){
case 'N' : i--; break;
case 'S' : i++; break;
case 'W' : j--; break;
case 'E' : j++; break;
}
int value = getHash(i, j);
if(set.contains(value)){
return true;
}else{
set.add(value);
}
}
return false;
}
public int getHash(int x, int y) {
return x * 20001 + y;
}
}
1497. 检查数组对是否可以被 k 整除
题目描述2
给你一个整数数组 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 <= 10^5
n 为偶数
-10^9 <= arr[i] <= 10^9
1 <= k <= 10^5
Solution2
我们将数组中的每个数 对 k 进行取模,并将得到的余数进行配对。
class Solution {
public boolean canArrange(int[] arr, int k) {
Map<Integer, Integer> mod = new HashMap<Integer, Integer>();
// 统计每个余数出现的次数
for(int num : arr){
mod.put((num % k + k) % k, mod.getOrDefault((num % k + k) % k, 0) + 1);
}
for(Map.Entry<Integer, Integer> entry : mod.entrySet()){
int t = entry.getKey();
int occ = entry.getValue();
//余数为t的次数必须和余数为k-t的次数相等,互相配对
if(t > 0 && mod.getOrDefault(k - t, 0) != occ){
return false;
}
}
//余数为0的次数也要是偶数
return mod.getOrDefault(0, 0) % 2 == 0;
}
}
Tips:
- 注意,负数取模后是负数,要把它们换算到正数上来,通过下面的方法得到在
[0, k-1]
的范围内的余数。
xk = (x % k + k) % k
- 用Entry依次取哈希表的每个键值
for(Map.Entry<Integer, Integer> entry : map.entrySet()){
int t = entry.getKey();
int occ = entry.getValue();
...
}
1498. 满足条件的子序列数目
题目描述3
给你一个整数数组 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
Solution3
二分+快速幂
注意子序列不要求连续。
-
先排序,因为只关心这个子序列的最小值和最大值,而不关心元素的相对顺序。
-
固定
i
,每次二分查找nums[i] + nums[r] <= target
的最大r
,则这个i
对应的子序列的数目为2^(r-i)
,加到答案中。理解
2^(r-i)
:从[i+1,r]
的这几个数字,每个数字都有「出现在子序列中」和「不出现在子序列中」两种状态,2*2*2...*2
一共r-i
个2相乘。注意要把2的幂次的计算结果先取模存起来,因为会溢出。遇到取模的技巧:
( a + b ) m o d P = [ ( a m o d P ) + ( b m o d P ) ] m o d P , ( a × b ) m o d P = [ ( a m o d P ) × ( b m o d P ) ] m o d P (a+b)modP=[(amodP)+(bmodP)]modP , (a×b)modP=[(amodP)×(bmodP)]modP (a+b)modP=[(amodP)+(bmodP)]modP,(a×b)modP=[(amodP)×(bmodP)]modP
class Solution {
static final int P = 1000000007;
public int numSubseq(int[] nums, int target) {
Arrays.sort(nums);
long ans = 0;
int n = nums.length;
// 把2的幂次的计算结果先取模存起来,因为会溢出
long[] pow = new long[n];
pow[0] = 1;
for(int i = 1; i < n; i++){
pow[i] = (pow[i-1] << 1) % P;
}
for(int i = 0; i < n; i++){
int l = i;
int r = n-1;
if(2 * nums[i] <= target){
//二分 找nums[i] + nums[r] <= target的最大r
while(l < r){
int mid = l + (r - l + 1) / 2;
if(nums[i] + nums[mid] > target){
r = mid - 1;
}else{
l = mid;
}
}
ans = (ans + pow[r-i]) % P;
}
}
return (int)ans;
}
}
1499. 满足不等式的最大值
题目描述4
给你一个数组 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 是严格递增的。
Solution4
双端队列。
化成yi-xi+yj+xj
,固定j
的值后,问题就变成了寻找区间内的最大值。
从大到小遍历j
,那么区间就是一个滑动窗口。可参考239. 滑动窗口最大值中用双端队列求解的方法。
参考题解:https://leetcode-cn.com/problems/max-value-of-equation/solution/java-on-shuang-xiang-dui-lie-by-qi-ge-tai-yang/
class Solution {
public int findMaxValueOfEquation(int[][] points, int k) {
Deque<Integer> queue = new ArrayDeque();
int re = Integer.MIN_VALUE;
for(int j = 0; j < points.length; j++){
// 每次根据对首元素更新答案,更新前要判断是否满足xj-xi<=k
// 从队列首部移除不满足不等式限制的项
while(queue.size() > 0){
int t = queue.getFirst();
if(points[j][0] - points[t][0] > k){
queue.removeFirst();
}else{
break;
}
}
//更新答案
if(queue.size() > 0){
re = Math.max(re, points[j][0] + points[j][1] + points[queue.getFirst()][1] - points[queue.getFirst()][0]);
}
while(queue.size() > 0){
int t = queue.getLast();
// if(points[j][0] - points[t][0] <= points[j][1] - points[t][1]){
// 新来一个点xj, yj,队列尾部为xt,yt,如果yj-xj>=yt-xt,也就是说存在更大的,移除队列尾部元素
if(points[j][1] - points[j][0] >= points[t][1] - points[t][0]){
queue.removeLast();
}else{
break;
}
}
queue.addLast(j);
}
return re;
}
}
欢迎关注公众号:GitKid,暂时每日分享LeetCode题解,在不断地学习中,公众号内容也在不断充实,欢迎扫码关注