Medium
题目描述
你和一帮盗贼正在计划抢劫银行。给你一个下标从 0 开始的整数数组 security ,其中 security[i] 是第 i 天执勤的警卫数量。日子从 0 开始编号。同时给你一个整数 time 。
如果第 i 天满足以下所有条件,我们称它为一个适合抢劫银行的 好日子 :
- 第
i天前和后都分别至少有time天。 - 第
i天前连续time天警卫数目都是非递增的。 - 第
i天后连续time天警卫数目都是非递减的。
更正式的,第 i 天是一个好日子当且仅当:security[i - time] >= security[i - time + 1] >= ... >= security[i] <= ... <= security[i + time - 1] <= security[i + time]。
返回一个数组,包含 所有 适合抢劫银行的好日子(下标从 0 开始)。返回的日子可以 任意 顺序排列。
示例 1:
输入:security = [5,3,3,3,5,6,2], time = 2
输出:[2,3]
解释:
第 2 天,我们有 security[0] >= security[1] >= security[2] <= security[3] <= security[4]。
第 3 天,我们有 security[1] >= security[2] >= security[3] <= security[4] <= security[5]。
没有其他日子符合这个条件,所以日子 2 和 3 是唯一的好日子。
示例 2:
输入:security = [1,1,1,1,1], time = 0
输出:[0,1,2,3,4]
解释:
因为 time 等于 0 ,所以每一天都是好日子,所以返回每一天。
示例 3:
输入:security = [1,2,3,4,5,6], time = 2
输出:[]
解释:
没有任何一天的前 2 天警卫数目是非递增的。
因此没有好日子,返回空数组。
提示:
1 <= security.length <= 10^50 <= security[i], time <= 10^5
解题思路
解题思路
这道题的关键是要判断每一天是否满足"前 time 天非递增,后 time 天非递减"的条件。
预处理优化思路
直观的暴力解法是对每个位置都检查前后各 time 天,但这样时间复杂度为 O(n × time),在最坏情况下可能超时。
更好的方法是使用预处理:
- 用
left[i]记录第 i 天之前连续非递增天数 - 用
right[i]记录第 i 天之后连续非递减天数
具体步骤
- 计算 left 数组:从左到右遍历,如果
security[i] <= security[i-1],则left[i] = left[i-1] + 1,否则left[i] = 0 - 计算 right 数组:从右到左遍历,如果
security[i] <= security[i+1],则right[i] = right[i+1] + 1,否则right[i] = 0 - 判断好日子:对于第 i 天,如果
left[i] >= time且right[i] >= time,则第 i 天是好日子
这种方法时间复杂度为 O(n),空间复杂度为 O(n),是最优解法。
代码实现
class Solution {
public:
vector<int> goodDaysToRobBank(vector<int>& security, int time) {
int n = security.size();
vector<int> left(n, 0), right(n, 0);
// 计算每个位置之前连续非递增的天数
for (int i = 1; i < n; i++) {
if (security[i] <= security[i - 1]) {
left[i] = left[i - 1] + 1;
}
}
// 计算每个位置之后连续非递减的天数
for (int i = n - 2; i >= 0; i--) {
if (security[i] <= security[i + 1]) {
right[i] = right[i + 1] + 1;
}
}
vector<int> result;
for (int i = time; i < n - time; i++) {
if (left[i] >= time && right[i] >= time) {
result.push_back(i);
}
}
return result;
}
};
class Solution:
def goodDaysToRobBank(self, security: List[int], time: int) -> List[int]:
n = len(security)
left = [0] * n
right = [0] * n
# 计算每个位置之前连续非递增的天数
for i in range(1, n):
if security[i] <= security[i - 1]:
left[i] = left[i - 1] + 1
# 计算每个位置之后连续非递减的天数
for i in range(n - 2, -1, -1):
if security[i] <= security[i + 1]:
right[i] = right[i + 1] + 1
result = []
for i in range(time, n - time):
if left[i] >= time and right[i] >= time:
result.append(i)
return result
public class Solution {
public IList<int> GoodDaysToRobBank(int[] security, int time) {
int n = security.Length;
int[] left = new int[n];
int[] right = new int[n];
// 计算每个位置之前连续非递增的天数
for (int i = 1; i < n; i++) {
if (security[i] <= security[i - 1]) {
left[i] = left[i - 1] + 1;
}
}
// 计算每个位置之后连续非递减的天数
for (int i = n - 2; i >= 0; i--) {
if (security[i] <= security[i + 1]) {
right[i] = right[i + 1] + 1;
}
}
IList<int> result = new List<int>();
for (int i = time; i < n - time; i++) {
if (left[i] >= time && right[i] >= time) {
result.Add(i);
}
}
return result;
}
}
var goodDaysToRobBank = function(security, time) {
const n = security.length;
const left = new Array(n).fill(0);
const right = new Array(n).fill(0);
// 计算每个位置之前连续非递增的天数
for (let i = 1; i < n; i++) {
if (security[i] <= security[i - 1]) {
left[i] = left[i - 1] + 1;
}
}
// 计算每个位置之后连续非递减的天数
for (let i = n - 2; i >= 0; i--) {
if (security[i] <= security[i + 1]) {
right[i] = right[i + 1] + 1;
}
}
const result = [];
for (let i = time; i < n - time; i++) {
if (left[i] >= time && right[i] >= time) {
result.push(i);
}
}
return result;
};
复杂度分析
| 复杂度类型 | 预处理解法 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(n) |
其中 n 是 security 数组的长度。时间复杂度为 O(n) 是因为我们只需要遍历数组三次(计算 left 数组、right 数组、找好日子)。空间复杂度为 O(n) 是因为需要额外的 left 和 right 数组。
相关题目
. Non-decreasing Array (Medium)
. Longest Mountain in Array (Medium)
. Find in Mountain Array (Hard)
. Find All Good Indices (Medium)