宇宙厂
据悉,宇宙厂将在年后对"不合理加班情况"进行治理,重点整理对象是「财经业务部门」。
这个"不合理加班情况"具体是指:财经业务部目前是极少数的可以提交周末双薪加班申请(且通过率很高)的部门,于是经常会有同事通过「周末申请加班,周一请假」来钻规则漏洞,赚取加班费。
这一神奇操作,基本上只被少数部门知晓,宇宙厂的其他业务部门首次听到该操作,反应和外人一样:大吃一鲸 🐳
不得不感叹,一些在黄金年代后半期迅速发展起来的公司,即使已经是以「高效、扁平化」为主节奏来运作,但背后仍然会有边边角角的人(或部门)游离在外,损害整体利益。
对此,你怎么看?除了「周末加班,周内休息」这样的"套利"操作,你还知晓什么"神奇"操作?欢迎评论区交流。
...
回归主题。
来一道和「社招」相关的算法题。
题目描述
平台:LeetCode
题号:209
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的连续子数组 ,并返回其长度。如果不存在符合条件的子数组,返回 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
前缀和 + 二分
利用 的数据范围为 ,可知前缀和数组满足「单调递增」。
我们先预处理出前缀和数组 sum
(前缀和数组下标默认从 开始),对于每个 而言,假设其对应的前缀和值为 ,我们将 视为子数组的右端点,问题转换为:在前缀和数组下标 范围内找到满足「值小于等于 」的最大下标,充当子数组左端点的前一个值。
利用前缀和数组的「单调递增」(即具有二段性),该操作可使用「二分」来做。
Java 代码:
class Solution {
public int minSubArrayLen(int t, int[] nums) {
int n = nums.length, ans = n + 10;
int[] sum = new int[n + 10];
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
for (int i = 1; i <= n; i++) {
int d = sum[i] - t;
int l = 0, r = i;
while (l < r) {
int mid = l + r + 1 >> 1;
if (sum[mid] <= d) l = mid;
else r = mid - 1;
}
if (sum[r] <= d) ans = Math.min(ans, i - r);
}
return ans == n + 10 ? 0 : ans;
}
}
C++ 代码:
class Solution {
public:
int minSubArrayLen(int t, vector<int>& nums) {
int n = nums.size(), ans = n + 10;
vector<int> sum(n + 10, 0);
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
for (int i = 1; i <= n; i++) {
int d = sum[i] - t;
int l = 0, r = i;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (sum[mid] <= d) l = mid;
else r = mid - 1;
}
if (sum[r] <= d) ans = min(ans, i - r);
}
return ans == n + 10 ? 0 : ans;
}
};
Python 代码:
class Solution:
def minSubArrayLen(self, t: int, nums: List[int]) -> int:
n, ans = len(nums), len(nums) + 10
s = [0] * (n + 10)
for i in range(1, n + 1):
s[i] = s[i - 1] + nums[i - 1]
for i in range(1, n + 1):
d = s[i] - t
l, r = 0, i
while l < r:
mid = (l + r + 1) // 2
if s[mid] <= d:
l = mid
else:
r = mid - 1
if s[r] <= d:
ans = min(ans, i - r)
return 0 if ans == n + 10 else ans
TypeScript 代码:
function minSubArrayLen(t: number, nums: number[]): number {
let n = nums.length, ans = n + 10;
const sum = new Array(n + 10).fill(0);
for (let i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
for (let i = 1; i <= n; i++) {
const d = sum[i] - t;
let l = 0, r = i;
while (l < r) {
const mid = l + r + 1 >> 1;
if (sum[mid] <= d) l = mid;
else r = mid - 1;
}
if (sum[r] <= d) ans = Math.min(ans, i - r);
}
return ans == n + 10 ? 0 : ans;
};
- 时间复杂度:预处理前缀和数组的复杂度为 ,遍历前缀和数组统计答案复杂度为 。整体复杂度为
- 空间复杂度:
滑动窗口
另外一个,复杂度比 更低的做法,是滑动窗口。
在一次遍历过程中,使用 j
和 i
分别代表窗口的左右端点,变量 c
用于记录窗口内的数值总和。
遍历过程其实就是右端点 i
不断右移的过程,每次将当前右端点 i
的值累加到 c
上,若累加后,左端点右移仍能满足「总和大于等于 t
」的要求,那么我们则让左端点 j
右移。
如此一来,我们便得到了每个右端点 i
固定时,下标最大的合法左端点 j
(若有)。所有合法窗口长度的最小值即是答案。
Java 代码:
class Solution {
public int minSubArrayLen(int t, int[] nums) {
int n = nums.length, ans = n + 10;
for (int i = 0, j = 0, c = 0; i < n; i++) {
c += nums[i];
while (j < i && c - nums[j] >= t) c -= nums[j++];
if (c >= t) ans = Math.min(ans, i - j + 1);
}
return ans > n ? 0 : ans;
}
}
C++ 代码:
class Solution {
public:
int minSubArrayLen(int t, vector<int>& nums) {
int n = nums.size(), ans = n + 10;
for (int i = 0, j = 0, c = 0; i < n; i++) {
c += nums[i];
while (j < i && c - nums[j] >= t) c -= nums[j++];
if (c >= t) ans = min(ans, i - j + 1);
}
return ans > n ? 0 : ans;
}
};
Python 代码:
class Solution:
def minSubArrayLen(self, t: int, nums: List[int]) -> int:
n, ans = len(nums), len(nums) + 10
j, c = 0, 0
for i in range(n):
c += nums[i]
while j < i and c - nums[j] >= t:
c -= nums[j]
j += 1
if c >= t:
ans = min(ans, i - j + 1)
return 0 if ans > n else ans
TypeScript 代码:
function minSubArrayLen(t: number, nums: number[]): number {
let n = nums.length, ans = n + 10;
for (let i = 0, j = 0, c = 0; i < n; i++) {
c += nums[i];
while (j < i && c - nums[j] >= t) c -= nums[j++];
if (c >= t) ans = Math.min(ans, i - j + 1);
}
return ans > n ? 0 : ans;
};
- 时间复杂度:
- 空间复杂度: