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^5
  • 0 <= security[i], time <= 10^5

解题思路

解题思路

这道题的关键是要判断每一天是否满足"前 time 天非递增,后 time 天非递减"的条件。

预处理优化思路

直观的暴力解法是对每个位置都检查前后各 time 天,但这样时间复杂度为 O(n × time),在最坏情况下可能超时。

更好的方法是使用预处理

  1. left[i] 记录第 i 天之前连续非递增天数
  2. right[i] 记录第 i 天之后连续非递减天数

具体步骤

  1. 计算 left 数组:从左到右遍历,如果 security[i] <= security[i-1],则 left[i] = left[i-1] + 1,否则 left[i] = 0
  2. 计算 right 数组:从右到左遍历,如果 security[i] <= security[i+1],则 right[i] = right[i+1] + 1,否则 right[i] = 0
  3. 判断好日子:对于第 i 天,如果 left[i] >= timeright[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 数组。

相关题目