Subarray

  • 一、前缀和
  • 1800.最大升序子数组和
  • 525.连续数组
  • 523.连续的子数组和
  • 974.和可被K整除的子数组
  • ★560.和为K的子数组
  • 1524.和为奇数的子数组数目
  • 1695.删除子数组的最大得分
  • 554.砖墙
  • 1588.所有奇数长度子数组的和
  • 1031.两个非重叠子数组的最大和
  • 1508.子数组和排序后的区间和
  • 1343.大小为K且平均值大于等于阈值的子数组数目
  • 2090.半径为K的子数组平均值
  • 152.乘积最大子数组
  • 1310.子数组异或查询
  • 1749.任意子数组和的绝对值的最大值
  • *689.三个无重叠子数组的最大和
  • 1712.将数组分成三个子数组的方案数
  • 二、滑动窗口
  • 643.子数组最大平均数I
  • 1695.删除子数组的最大得分
  • 209.长度最小的子数组
  • 992.K个不同整数的子数组
  • 1248.统计「优美子数组」
  • 560.和为K的子数组
  • 930.和相同的二元子数组
  • 795.区间子数组个数
  • 713.乘积小于K的子数组
  • 2261.含最多K个可整除元素的子数组
  • 三、动态规划
  • 53.最大子数组和
    • 918.环形子数组的最大和
  • [2321. 拼接数组的最大分数](https://leetcode.cn/problems/maximum-score-of-spliced-array/)
  • 1191.K次串联后最大子数组之和
  • 1477.找两个和为目标值且不重叠的子数组
  • 898.子数组按位或操作
  • 978.最长湍流子数组
  • 718.最长重复子数组
  • 四、单调栈
  • 907.子数组的最小值之和
  • 581.最短无序连续子数组
  • ★2104.子数组范围和
  • 1856.子数组最小乘积的最大值
  • 四、综合题:前缀和、单调队列,滑动窗口。
  • ★862.和至少为K的最短子数组
  • 1438.绝对差不超过限制的最长连续子数组
  • 1793.好子数组的最大分数
  • *1157.子数组中占绝大多数的元素
  • 1186.删除一次得到子数组最大和
  • *1330.翻转子数组得到最大的数组值
  • *1460.通过翻转子数组使两个数组相等
  • *1526.形成目标数组的子数组最少增加次数
  • *1546.和为目标值且不重叠的非空子数组的最大数目
  • 1567.乘积为正数的最长子数组长度
  • *1569.将子数组重新排序得到同一个二叉查找树的方案数
  • *1574.删除最短的子数组使剩余数组有序
  • 1630.等差子数组
  • 1764.通过连接另一个数组的子数组得到一个数组
  • 2261.含最多K个可整除元素的子数组
  • [6248. 统计中位数为 K 的子数组](https://leetcode.cn/problems/count-subarrays-with-median-k/)
    • 581.最短无序连续子数组
    • 1574.删除最短的子数组使剩余数组有序

一、前缀和

1800.最大升序子数组和

class Solution:def maxAscendingSum(self, nums: List[int]) -> int:ans, left, pre = 0, 0, -1acc = [0] + list(accumulate(nums))for i, x in enumerate(nums):if x <= pre: left = ipre = xans = max(ans, acc[i+1] - acc[left])return ans

525.连续数组

把 0 当作 -1 来统计前缀和,用哈希表记录前缀和第一次出现的下标,两个位置的前缀和相同说明,中间子数组(不包含第一个包含第二个位置)的和为 0, 也就是 0 和 1 个数相等。

class Solution:def findMaxLength(self, nums: List[int]) -> int:    ans, acc, d = 0, 0, {0:-1} # 求长度 -1,子数组个数 1for i, x in enumerate(nums):acc += 1 if x else -1 # 把 0 当作 -1 来统计        if acc in d: ans = max(ans, i - d[acc]) # 更新长度else: d[acc] = i  # key 前缀和,value 索引,保存第一次出现时的下标      return ans
class Solution {public int findMaxLength(int[] nums) {int ans = 0, n = nums.length, acc = 0;Map<Integer, Integer> map = new HashMap<>();map.put(0, -1);for (int i = 0; i < n; i++){acc += (nums[i] == 0) ? -1 : 1;if (map.containsKey(acc)){ans = Math.max(ans, i - map.get(acc));} else map.put(acc, i);}return ans;}
}

523.连续的子数组和

两个整数 a、b,若它们除以整数 m 所得的余数相等,则称 a 与 b 对于模 m 同余或 a 同余于 b 模 m
记作 a≡b (mod m)
读作 a 同余于 b 模 m,或读作 a 与 b 对模 m 同余。

class Solution:def checkSubarraySum(self, nums: List[int], k: int) -> bool:acc, d = 0, {0:-1} # value 作为下标用for i, x in enumerate(nums):acc += x            key = acc % k # 同余if key in d:if i - d[key] >= 2: return Trueelse: d[key] = i  # 保存最小索引        return False
class Solution {public boolean checkSubarraySum(int[] nums, int k) {int acc = 0;Map<Integer, Integer> map = new HashMap<>();map.put(0, -1);for (int i = 0; i < nums.length; i++){acc += nums[i];int rem = acc % k;if (map.containsKey(rem)){if (i - map.get(rem) >= 2) return true;}else map.put(rem, i);           }return false;}
}

974.和可被K整除的子数组

同余定理:(a - b) % k = 0 等价于 a % k = b % k

class Solution:def subarraysDivByK(self, nums: List[int], k: int) -> int:   ans, acc, rem = 0, 0, [0] * k # 保存余数      for x in nums:rem[acc % k] += 1 # 统计个数用 {0 : 1}          acc += x         ans += rem[acc % k]  return ans
class Solution {public int subarraysDivByK(int[] nums, int k) {int ans = 0, acc = 0;int[] d = new int[k];d[0] = 1;for (int x : nums){acc += x;ans += d[(acc % k + k) % k]++;}return ans;}
}
class Solution {public int numOfSubarrays(int[] arr) {int[] cnt = {0, 0};int acc = 0, ans = 0;for (int x : arr) {cnt[acc % 2]++;acc += x;ans += cnt[1 - acc % 2]; ans %= 1000000007;}return ans;}
}

★560.和为K的子数组

借鉴 两数和 的思路 ,利用哈希表。这里是两个前缀和的差。

遍历数组,根据当前项前缀和 acc,在 map 中寻找差为 k 的历史前缀和 pre 即 acc - k。acc - pre = k,求区间的和转换为求前缀和的差。
添加哨兵 d[0] = 1,解决前缀和为 k 的情况。

class Solution:def subarraySum(self, nums: List[int], k: int) -> int:ans, acc, d = 0, 0, defaultdict(int)for x in nums:d[acc] += 1 # 统计前一个前缀和,默认 {0 : 1}acc += x # 当前项的前缀和ans += d[acc - k] # 在 d 中找差为 k 的前缀和 pre, acc - pre = k,即 pre = acc - kreturn ans

1524.和为奇数的子数组数目

class Solution:def numOfSubarrays(self, arr: List[int]) -> int:acc, ans, d = 0, 0, [0, 0]for x in arr:d[acc % 2] += 1acc += xans += d[1 - acc % 2]     return ans % 1000000007
class Solution {public int subarraySum(int[] nums, int k) {int res = 0, acc = 0;Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; i++){map.put(acc, map.getOrDefault(acc, 0) + 1);acc += nums[i];res += map.getOrDefault(acc - k, 0);            }return res;}
}

1695.删除子数组的最大得分

class Solution:def maximumUniqueSubarray(self, nums: List[int]) -> int:res, left, d = 0, 0, defaultdict(int)       pre = [0]+list(accumulate(nums)) # 哨兵 前缀和for i, x in enumerate(nums):if x in d and d[x] >= left: left = d[x] + 1 # 只考虑从 left 开始的重复else: res = max(res, pre[i + 1] - pre[left])d[x] = i        return res

554.砖墙

class Solution:def leastBricks(self, wall: List[List[int]]) -> int:        d = {}for w in wall:acc = 0 # 用前缀和表示砖缝for x in w[:-1]: # 不包含最后一块砖acc += xd[acc] = d.get(acc, 0) + 1return len(wall) - max(d.values(), default = 0)
class Solution {public int leastBricks(List<List<Integer>> wall) {Map<Integer, Integer> cnt = new HashMap<>();for (List<Integer> w : wall) {int acc = 0;for (int i = 0; i < w.size() - 1; i++) {acc += w.get(i);cnt.put(acc, cnt.getOrDefault(acc, 0) + 1);}}int max = 0;for (int i : cnt.values()){max = Math.max(max, i);}return wall.size() - max;}
}

1588.所有奇数长度子数组的和

class Solution:def sumOddLengthSubarrays(self, arr: List[int]) -> int:     ans, pre, n = 0, 0, len(arr)acc = list(accumulate(arr))            for i in range(n):# 以下标 i 开头的长度为奇数的子数组和ans += sum(acc[j] - pre for j in range(i, n, 2))pre = acc[i]return ans
class Solution {public int sumOddLengthSubarrays(int[] arr) {int n = arr.length, pre = 0, ans = 0;for (int i = 1; i < n; i++) arr[i] += arr[i - 1]; for (int i = 0; i < n; i++){for (int j = i; j < n; j += 2){ans += arr[j] - pre;}pre = arr[i];}return ans; }
}

1031.两个非重叠子数组的最大和

class Solution:def maxSumTwoNoOverlap(self, nums: List[int], firstLen: int, secondLen: int) -> int:n, a, b = len(nums), firstLen, secondLenacc = list(accumulate(nums))            ans = acc[a + b - 1]maxf = acc[a - 1]maxs = acc[b - 1]for i in range(a + b, n):maxf = max(maxf, acc[i-b] - acc[i-a-b]) # acc[a] - acc[0] 第一段maxs = max(maxs, acc[i-a] - acc[i-a-b]) # acc[b] - acc[0] 第一段ans = max(ans, max(maxf + acc[i] - acc[i-b], maxs + acc[i] - acc[i-a])) # 第一段 + 笫二段 a + b, b + a        return ans
class Solution {public int maxSumTwoNoOverlap(int[] nums, int firstLen, int secondLen) {int a = firstLen, b = secondLen, n = nums.length;for (int i = 1; i < n; i++) nums[i] += nums[i-1];        int ans = nums[a+b-1], x = nums[a-1], y = nums[b-1];for (int i = a + b; i < n; i++){x = Math.max(x, nums[i-b] - nums[i-a-b]);y = Math.max(y, nums[i-a] - nums[i-a-b]);ans = Math.max(ans, Math.max(x + nums[i] - nums[i-b], y + nums[i] - nums[i-a]));           }return ans;}
}

1508.子数组和排序后的区间和

class Solution:def rangeSum(self, nums: List[int], n: int, left: int, right: int) -> int:acc = [0]+list(accumulate(nums))res = sorted([acc[j] - acc[i]  for i in range(n) for j in range(i+1, n+1)])        return sum(res[left-1:right]) % (10**9 + 7)

1343.大小为K且平均值大于等于阈值的子数组数目

class Solution:def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int:# acc = [0] + list(accumulate(arr))# ans = 0# for i in range(len(arr) - k + 1):#     if (acc[i + k] - acc[i]) // k >= threshold:#         ans += 1# return ansans, acc = 0, sum(arr[:k-1]) # 差一个for i, x in enumerate(arr[k-1:]):acc += x            if acc // k >= threshold: ans += 1acc -= arr[i]return ans

2090.半径为K的子数组平均值

class Solution:def getAverages(self, nums: List[int], k: int) -> List[int]:        n, t = len(nums), 2*k + 1ans = [-1] * nacc = sum(nums[:t-1]) # 差一个for i in range(k, n - k):# i = k 时 acc[2*k+1] - acc[0] 对应 nums[2*k] 的前缀和acc += nums[i+k] # t 个 补一个ans[i] = acc // tacc -= nums[i-k] # 减一个return ans

152.乘积最大子数组

class Solution:def maxProduct(self, nums: List[int]) -> int:acc, ans, negmax = 1, -inf, -inf # 记录负数最大值for x in nums:acc *= x            ans = max(ans, acc, x) # nums 和 acc 的最大值 更新答案if acc == 0: # 重新初始化acc, negmax = 1, -infelif acc < 0:if negmax != -inf:ans = max(ans, acc // negmax)negmax = max(negmax, acc)return ans

1310.子数组异或查询

class Solution:def xorQueries(self, arr: List[int], queries: List[List[int]]) -> List[int]:acc = [0]for i, x in enumerate(arr):acc.append(acc[-1]^x)       return [acc[i]^acc[j+1] for i, j in queries]

1749.任意子数组和的绝对值的最大值

class Solution:def maxAbsoluteSum(self, nums: List[int]) -> int:acc = list(accumulate(nums))return max(abs(max(acc) - min(acc)), abs(max(acc)), abs(min(acc)))

*689.三个无重叠子数组的最大和

1712.将数组分成三个子数组的方案数

class Solution:def waysToSplit(self, nums: List[int]) -> int:n, ans = len(nums), 0acc = list(accumulate(nums))for i in range(n):if acc[i] > acc[-1] // 3: break # 剪枝# 左边: nums[:i+1]和 acc[i], i 确定了第一个# 中间: 右边界最小为 a 即前一个的二倍,最大是 acc[i] + (acc[-1] - acc(i)) // 2, 共有 b - a 种选择# 中间:[i+1, x] x ∈ [a, b)#[0, i] (i, x) [x, -1]a = bisect_left(acc, 2 * acc[i], i + 1, n - 2)# 剩余的中间位置b = bisect_right(acc, (acc[i] + acc[-1] ) / 2 , a, n - 1)ans += b - areturn ans % 1000000007

二、滑动窗口

643.子数组最大平均数I

class Solution:def findMaxAverage(self, nums: List[int], k: int) -> float:     s = res = sum(nums[:k])  n = len(nums)for i in range(k, n):s += nums[i] - nums[i - k] # 固定窗口移动res = max(res, s)return res / k
class Solution {public double findMaxAverage(int[] nums, int k) {int sum = 0;for (int i = 0; i < k; i++) sum += nums[i];int average = sum;for (int i = k; i < nums.length; i++){sum += nums[i] - nums[i - k];average = Math.max(average, sum);} return (double)average / k;}
}

1695.删除子数组的最大得分

class Solution:def maximumUniqueSubarray(self, nums: List[int]) -> int:d, left, sum_, res = defaultdict(bool), 0, 0, 0for i, v in enumerate(nums):if d[v]:res = max(res, sum_)while nums[left] != v: sum_ -= nums[left]d[nums[left]] = Falseleft += 1left += 1else: sum_ += vd[v] = Truereturn max(res, sum_)# 前缀和res, left, d = 0, 0, defaultdict(int)       pre = [0]+list(accumulate(nums)) # 哨兵 前缀和for i, x in enumerate(nums):if x in d and d[x] >= left: left = d[x] + 1 # 只考虑从 left 开始的重复else: res = max(res, pre[i + 1] - pre[left])d[x] = i        return res
class Solution {public int maximumUniqueSubarray(int[] nums) {int res = 0, left = 0;Map<Integer, Integer> map = new HashMap<>();int[] pre = new int[nums.length + 1]; for (int i = 0; i < nums.length; i++){int x = nums[i];pre[i + 1] = pre[i] + x; // 求前缀和if (map.containsKey​(x) && map.get(x) >= left) left = map.get(x) + 1;else res = Math.max(res, pre[i + 1] - pre[left]);map.put(x, i);}return res;}
}

209.长度最小的子数组

class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:res, left, s = inf, 0, 0 # ▲ 最大值可取 len(nums) + 1for i, x in enumerate(nums):s += xwhile s >= target:  # 满足前提条件找最小         res = min(res, i - left + 1)s -= nums[left]left += 1return res if res != inf else 0 # ▲ sum(nums) < target 的情况
class Solution {public int minSubArrayLen(int target, int[] nums) {int res = nums.length + 1, left = 0, sum = 0;for (int i = 0; i < nums.length; i++){sum += nums[i];while (sum >= target){ // 满足条件中求最小res = Math.min(res, i - left + 1); // 更新结果sum -= nums[left];left++; // 收缩左边界}}return res == nums.length + 1 ? 0 : res;}
}

992.K个不同整数的子数组

把 「恰好」 转换成为 「最多」,「最多存在 K 个不同整数的子区间的个数」与「恰好存在 K 个不同整数的子区间的个数」的差恰好等于「最多存在 K - 1 个不同整数的子区间的个数」。

class Solution {public int subarraysWithKDistinct(int[] nums, int k) {return atMostK(nums, k) - atMostK(nums, k - 1);}private int atMostK(int[] nums, int k){ // 解法:同 904. 水果成篮int n = nums.length, left = 0, res = 0;int[] d = new int[n + 1]; for (int i = 0; i < n; i++){if (d[nums[i]] == 0) k--;d[nums[i]]++;while (k == -1){               d[nums[left]]--;if (d[nums[left]] == 0) k++;left++;}res += i - left; // [left, i) 区间的长度就是对结果的贡献}return res;}
}

1248.统计「优美子数组」

class Solution:def numberOfSubarrays(self, nums: List[int], k: int) -> int:return self.atMostK(nums, k) - self.atMostK(nums, k - 1)def atMostK(self, nums, k):# ans, left = 0, deque([-1]) # for i, x in enumerate(nums):#     if x % 2: left.append(i); k -= 1#     if k < 0: left.popleft(); k += 1                #     ans += i - left[0]ans, left = 0, -1 for i, x in enumerate(nums):k -= x % 2 # 奇数减一while k < 0: left += 1 # 先加后判断             k += nums[left] % 2ans += i - left # + 以索引 i 结尾的子数组个数return ans

560.和为K的子数组

k 可为负,不象 930。

930.和相同的二元子数组

前缀和数组 acc,子数组 (i, j] 的和为 goal,即:acc[j] − acc[i] = goal。枚举 j ,每次查询满足该等式的 i 的数量。

用哈希表记录每一种前缀和出现的次数,当前枚举到元素 nums[j],查询哈希表中元素 acc[j] − goal,即对应了以当前 j 值为右边界的满足条件的子数组的数量。最后这些元素的总数量即为所有和为 goal 的子数组数量。

实时地更新哈希表,以防止出现 i ≥ j 的情况。

class Solution:def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:## 方法一:前缀和 哈希表ans = acc = 0 # 计算前缀和,不用实现前缀和数组d = defaultdict(int) # 统计出现前缀和 acc 的个数for x in nums:            d[acc] += 1acc += xans += d[acc - goal]return ans
class Solution {public int numSubarraysWithSum(int[] nums, int goal) {int ans = 0, acc = 0;Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; i++){map.put(acc, map.getOrDefault(acc, 0) + 1);acc += nums[i];ans += map.getOrDefault(acc - goal, 0);}return ans;}
}

求和正好是 goal 的问题转换为求和 ≤ goal 的问题。

class Solution:def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:## 方法二:滑动窗口 atMostKdef atMostK(nums, k): # 求和 ≤ goal 的子数组 if k < 0: return 0 # goal = 0 的情况 left = res = 0for i, x in enumerate(nums):k -= xwhile k < 0:k += nums[left]left += 1res += i - left + 1 # 元素个数也是了数组的个数return resreturn atMostK(nums, goal) - atMostK(nums, goal - 1)
class Solution {public int numSubarraysWithSum(int[] nums, int goal) {return atMostK(nums, goal) - atMostK(nums, goal - 1);}private int atMostK(int[] nums, int k){if (k < 0) return 0;int res = 0, left = 0;for (int i = 0; i < nums.length; i++){k -= nums[i];while (k < 0){k += nums[left];left++;}res += i - left + 1;}return res;}
}

795.区间子数组个数

最大元素在范围 [left, right] 内,即:元素 ≤ R 且至少包含一个 ≥ L 的子数组。
转换为所有元素 ≤ R 的子数组,再从中减去只包含元素 < L 即:≤ L - 1 的子数组。

class Solution:def numSubarrayBoundedMax(self, nums: List[int], left: int, right: int) -> int:def atMostK(k):ans = left = 0for i, x in enumerate(nums):if x > k: left = i + 1ans += i - left + 1 # ? 加多少都行,可能是两个相减抵消了。                     return ansreturn atMostK(right) - atMostK(left - 1)class Solution:def numSubarrayBoundedMax(self, nums: List[int], left: int, right: int) -> int:start, res, cnt = -1, 0, 0for i, x in enumerate(nums): if x > right: start = i # 重新开始if x >= left: cnt = i - start # 基数res += cntreturn res

713.乘积小于K的子数组

class Solution:def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:ans, prod, left = 0, 1, 0for i, x in enumerate(nums):prod *= xwhile left <= i and prod >= k: # k = 0 的情况prod //= nums[left]left += 1ans += i - left + 1return ans

2261.含最多K个可整除元素的子数组

class Solution:def countDistinct(self, nums: List[int], k: int, p: int) -> int:       s, n = set(), len(nums)for i in range(n):count = 0       for j in range(i, n):if nums[j] % p == 0: count += 1          if count > k: breaks.add(tuple(nums[i:j+1]))                  return len(s)
class Solution {public int countDistinct(int[] nums, int k, int p) {Set<List<Integer>> set = new HashSet<>();    // 自动去重for (int i = 0; i < nums.length; i++) {int count = 0;  // 计数List<Integer> list = new ArrayList<>();for(int j = i; j < nums.length; j++) {if(nums[j] % p == 0) count++;if(count > k) break; list.add(nums[j]);  set.add(new ArrayList(list));}}return set.size();   }
}

三、动态规划

53.最大子数组和

dp[i] 表示以 nums[i] 结尾的子数组的最大和
dp[i + 1] = max(nums[i], dp[i] + nums[i])

class Solution:def maxSubArray(self, nums: List[int]) -> int:pre = ans = -inffor x in nums:pre = max(pre + x, x)ans = max(ans, pre)return ans dp = [-inf]for x in nums:dp.append(max(x, dp[-1] + x))return max(dp)
class Solution {public int maxSubArray(int[] nums) {int pre = nums[0], ans = pre;for (int i = 1; i < nums.length; i++){pre = Math.max(nums[i], pre + nums[i]);ans = Math.max(ans, pre);}return ans;}
}

918.环形子数组的最大和

premax/premin:以当前i元素结尾的子数组的和的最大最小值
maxsum/minsum:数组中子数组和的最大最小值

无环,即为 53. 最大子数组和
有环,分首尾两段,要使两端之和最大,必须让中间的子数组最小,即最大子数组和为:sum(nums) - min_,问题转换为求最小子数组和。

class Solution:def maxSubarraySumCircular(self, nums: List[int]) -> int:maxsum = minsum = premax = premin = sum_ = nums[0]for i in range(1, len(nums)):           premax = max(nums[i], premax + nums[i])premin = min(nums[i], premin + nums[i])maxsum = max(maxsum, premax)minsum = min(minsum, premin)sum_ += nums[i]return max(maxsum, sum_ - minsum if sum_ != minsum else maxsum)# [-3,-2,-3] 最小和 = 和 的情况

前缀和、单调队列,滑动窗口。

class Solution:def maxSubarraySumCircular(self, nums: List[int]) -> int:n = len(nums)        ans = nums[0]acc = [ans]q = deque([0]) for i in range(1, 2 * n):acc.append(acc[i - 1] + nums[i % n])if i - q[0] > n: q.popleft()ans = max(ans, acc[i] - acc[q[0]])# 单调栈while q and acc[i] <= acc[q[-1]]: q.pop()q.append(i)return ans
class Solution {public int maxSubarraySumCircular(int[] nums) {int n = nums.length;int[] acc = new int[2 * n];acc[0] = nums[0];int ans = nums[0];Deque<Integer> q = new ArrayDeque<>();q.add(0);for (int i = 1; i < 2*n; i++){acc[i] = acc[i - 1] + nums[i % n];if (i - q.peek() > n) q.poll();ans = Math.max(ans, acc[i] - acc[q.peek()]); // 小的尽可能左移while (!q.isEmpty() && acc[i] <= acc[q.peekLast()]) q.pollLast();q.add(i);}return ans;}
}

2321. 拼接数组的最大分数

class Solution:def maximumsSplicedArray(self, nums1: List[int], nums2: List[int]) -> int:n = len(nums1)s, t = sum(nums1), sum(nums2)        premax = mx = 0premin = mn = inf      for a, b in zip(nums1, nums2):x = a - bpremax = max(x, premax + x)premin = min(x, premin + x)mx = max(mx, premax)mn = min(mn, premin)return max(t + mx, s - mn)

1191.K次串联后最大子数组之和

分三种情况
1、k = 1: 同 53 题 注意 答案非负数
2、k = 2: 最大前缀和 + 最大后缀和, 其实两个合起来同 1
3、k > 2: sum < 0, 同 2;否则 2 答案 + sum * (k - 2)

class Solution:def kConcatenationMaxSum(self, arr: List[int], k: int) -> int:a = arr * min(k, 2)ans = pre = 0 # 答案最小为 0for i, x in enumerate(a):pre = max(x, pre + x)ans = max(ans, pre)s = sum(arr)if s > 0 and k > 2:ans = max(ans, s * (k - 2) + ans)return ans % 1000000007

1477.找两个和为目标值且不重叠的子数组

借鉴 两数之和 的解法,寻找和为 target 的子数组。
用动态规划找两个互不重叠的、长度和最小的子数组 。

dp[i] 表示当前位置 i 之前最短的一个长度。
所以我们的目的就是找出另外一个最短的满足条件的子数组。

class Solution:def minSumOfLengths(self, arr: List[int], target: int) -> int:acc, d, n = 0, {0:-1}, len(arr)ans, dp = n + 1, [n] * nfor i, x in enumerate(arr):acc += x            y = acc - targetif y in d:curlen = i - d[y]     # 更新答案,当前子数组长度 + 前一个 dp[d[y]], 同时保证了不重复。           ans = min(ans, curlen + dp[d[y]])                dp[i] = min(curlen, dp[i - 1]) # 更新当前的最小长度 状态转移 ⑴else: dp[i] = dp[i - 1] # 状态转移 ⑵d[acc] = ireturn -1 if ans == n + 1 else ans

898.子数组按位或操作

class Solution:def subarrayBitwiseORs(self, arr: List[int]) -> int:      ans, pre = set(), set()for i, x in enumerate(arr):cur = {x}for y in pre:   cur.add(x | y) # 按位或ans |= cur # 并集   pre = cur   return len(ans)

978.最长湍流子数组

class Solution:def maxTurbulenceSize(self, arr: List[int]) -> int:ans, left, n, pre = 1, 0, len(arr), -1 # 开始不确定for i in range(1, n):# 分三种情况if arr[i] > arr[i - 1]: cur = 1elif arr[i] < arr[i - 1]: cur = -1else: cur = 0   if cur == 0: left = ielif cur == pre: left = i - 1# cur = 1 if arr[i] > arr[i - 1] else -1 if arr[i] < arr[i - 1] else 0 # left = i if cur == 0 else i - 1 if cur == pre else left            pre, ans = cur, max(ans, i - left + 1)return ans
class Solution {public int maxTurbulenceSize(int[] arr) {int ans = 1, pre = -1, left = 0;for (int i = 1; i < arr.length; i++){int cur = 0;if (arr[i] > arr[i - 1]) cur = 1;else if (arr[i] < arr[i - 1]) cur = -1;else cur = 0;if (cur == 0) left = i;else if (pre == cur) left = i -1;ans = Math.max(ans, i - left + 1);pre = cur;}return ans;}
}

718.最长重复子数组

class Solution:def findLength(self, nums1: List[int], nums2: List[int]) -> int:n, m = len(nums2), len(nums1)#dp = [[0] * (n + 1) for _ in range(m + 1)]dp = [0] * (n + 1)ans = 0for i in range(m):# # 逆序遍历可降维,正序需要滚动数组# for j in range(n-1,-1,-1):#     if nums1[i] == nums2[j]:#         dp[j+1] = dp[j] + 1#         ans = max(ans, dp[j+1])#     else:dp[j+1] = 0tmp = [0] * (n + 1) # 滚动数组for j in range(n):if nums1[i] == nums2[j]:#dp[i+1][j+1] = dp[i][j] + 1#ans = max(ans, dp[i+1][j+1])tmp[j+1] = dp[j] + 1ans = max(ans, tmp[j+1])dp = tmpreturn ans

四、单调栈

907.子数组的最小值之和

数组中每个元素 E = arr[i] 作为最小值的范围 (L, R) ,子数组个数为 count = (i - L) * (R - i ),元素 E 的总贡献值为 arr[i] * count。计算出每个元素的贡献值,然后求和。

利用单调栈 q,找出 arr[q[-1]] 作为最小值的范围,即 (q[-2], i) , Subarray 个数,分两步 左 i - j 个选择,右 j - q[-1] 个选择。

class Solution:def sumSubarrayMins(self, arr: List[int]) -> int:arr.append(-1) # 添加哨兵,最后会计算 q 中所有的下标,除 -1 和 len(arr) - 1 外。q, ans = [-1], 0 # -1 充当开头的下标,事实是末尾的下标,arr[-1] = -1。for i, x in enumerate(arr):while x < arr[q[-1]]: # 遇到更小的数作为右边界计算前面的值,当前数先加入后计算。j = q.pop()ans += arr[j]*(i - j)*(j - q[-1])q.append(i)return ans % (10**9+7)
class Solution {public int sumSubarrayMins(int[] arr) {        int ans = 0, n = arr.length, mod = 1_000_000_007;// Deque 18 ms// Deque<Integer> q = new ArrayDeque<>();// for (int i = 0; i <= n; i++){//     int cur = i == n ? -1 : arr[i]; // 相当于加了哨兵//     while (!q.isEmpty() && cur < arr[q.peek()]) {//         int j = q.pop();//         int k =  q.isEmpty() ? -1 : q.peek();            //         long tmp = (long)arr[j] * (j - k) * (i - j) % mod;//         ans = (ans + (int)tmp) % mod;                //     }//     q.push(i);// Array 6 msint idx = -1;int[] q = new int[n];for (int i = 0; i <= n; i++){ int cur = i == n ? 0 : arr[i];while (idx != -1 && cur < arr[q[idx]]) {// k 左边界,i 右边界,j 中间int j = q[idx--];int k =  idx == -1 ? -1 : q[idx];            long tmp = (long)arr[j] * (j - k) * (i - j) % mod;ans = (ans + (int)tmp) % mod;                }q[++idx] = i;}return ans;}
}
class Solution {public int sumSubarrayMins(int[] arr) {long res = 0L;for (int i = 0; i < arr.length; i++){int min = arr[i];for (int j = i; j < arr.length; j++){min = Math.min(min, arr[j]);res += min;res  %= 1000000007; // 过 78 个// if (res > 1000000007) res -= 1000000007; // 过 83 个}}return (int)res % 1000000007;}
}

581.最短无序连续子数组

class Solution:def findUnsortedSubarray(self, nums: List[int]) -> int:q, n = [], len(nums)left, right = len(nums) - 1, 0mx, mn = nums[0], nums[-1]for i, x in enumerate(nums):'''# 利用单调栈找左边界while q and x < nums[q[-1]]:left = min(left, q.pop())q.append(i)'''# 逆序遍历,记录大于当前最小值的下标y = nums[-i -1]if y > mn: left = n-i-1else: mn = y# 记录小于当前最大值的下标if x < mx: right = ielse: mx = x           return 0 if right == 0 else right - left + 1
class Solution {public int findUnsortedSubarray(int[] nums) {int n = nums.length, left = n - 1, right = 0, max = nums[0], min = nums[n - 1];for (int i = 0; i < n; i++){int x = nums[i], y = nums[n-i-1];if (y > min) left = n-i-1;else min = y;if (x < max) right = i;else max = x;}return right == 0 ? 0 : right - left + 1;}
}

★2104.子数组范围和

所有子数组范围的和 = 所有子数组的最大值的和 A - 所有子数组的最小值的和 B。
A = sum(a * k for a in nums) # k 是以 a 为最大值的子数组数目,ak 即 a 对和的贡献。

假设 a = nums[i],a 的左右第一个比 a 大的数的索引分别是 left 和 right,k = (right − i) × (i − left),其中包含 i 的区间左端点数为 i - left 个,右端点数为 right - i 个,分步为乘法。a 作为最大值的最大区间为 [left + 1, right - 1]

class Solution:def subArrayRanges(self, nums: List[int]) -> int:nums.append(inf)# 单调递减栈q, res = [-1], 0for r, e in enumerate(nums):# 遇到比栈顶大的数,终结栈顶元素。while q and e > nums[q[-1]]: i = q.pop()left = q[-1] # if q else -1 # 在 q 预先加入 -1 res += nums[i] * (i - left) * (r - i)q.append(r)# 单调递增栈q, nums[-1] = [-1], -inffor r, e in enumerate(nums):while q and e < nums[q[-1]]:i = q.pop()left = q[-1] res -= nums[i] * (i - left) * (r - i)q.append(r)return res# 方法:暴力+滑动窗口,从起点开始遍历更新最值n, res = len(nums), 0for i in range(n):mi = ma = nums[i]for j in range(i + 1, n):ma = max(ma, nums[j])mi = min(mi, nums[j])res += ma - mireturn ans
class Solution {public long subArrayRanges(int[] nums) {    int n = nums.length;    long res = 0;ArrayDeque<Integer> q = new ArrayDeque<>();int[] x = Arrays.copyOf(nums, n + 1);x[n] = Integer.MAX_VALUE;for (int i = 0; i <= n; i++){           while (!q.isEmpty() && x[i] > x[q.peek()]){int j = q.pop();res += (long)x[j] * (j - (q.isEmpty() ? -1 : q.peek())) * (i - j);}q.push(i);}q.clear();x[n] = Integer.MIN_VALUE;for (int i = 0; i <= n; i++){           while (!q.isEmpty() && x[i] < x[q.peek()]){int j = q.pop();res -= (long)x[j] * (j - (q.isEmpty() ? -1 : q.peek())) * (i - j);}q.push(i);}return res;// 优化:用数组代替栈// int n = nums.length, top = 0;// long res = 0;// int[] q = new int[n + 1]; // 存储下标,这里先放个 -1// q[0] = -1; // for (int i = 0; i <= n; i++){//     while (top > 0 && (i == n || nums[q[top]] < nums[i])){//         int j = q[top--];//         res += 1L * nums[j] * (i - j) * (j - q[top]);//     }//     q[++top] = i;// }// top = 0;// q[0] = -1; // for (int i = 0; i <= n; i++){//     while (top > 0 && (i == n || nums[q[top]] > nums[i])){//         int j = q[top--];//         res -= 1L * nums[j] * (i - j) * (j - q[top]);//     }//     q[++top] = i;// }// return res;        }
}

1856.子数组最小乘积的最大值

枚举「最小值」,数组中的每个元素 nums[i] 作为最小值,使用单调栈快速寻找左右两边不小于他的边界。

class Solution:def maxSumMinProduct(self, nums: List[int]) -> int:mod, n = 10**9 + 7, len(nums)left, right = [0] * n, [n - 1] * n        q = list() # 单调栈 确定左右边界for i, num in enumerate(nums):while q and nums[q[-1]] >= num: # 非严格             right[q[-1]] = i - 1 # right[i] 是右侧最近的小于等于 nums[i] 的元素下标 q.pop()if q:                left[i] = q[-1] + 1 # left[i] 是左侧最近的严格小于 nums[i] 的元素下标q.append(i)# 前缀和acc = [0] + list(accumulate(nums))        ans = max((acc[right[i] + 1] - acc[left[i]]) * x for i, x in enumerate(nums))return ans % mod

四、综合题:前缀和、单调队列,滑动窗口。

★862.和至少为K的最短子数组

class Solution:def shortestSubarray(self, nums: List[int], k: int) -> int:n = len(nums)ans, q = n + 1, collections.deque() # 双端队列acc = [0] + list(accumulate(nums)) # 前缀和for i, x in enumerate(acc):# [84,-37,32,40,95] [0, 84, 47, 79, 119, 214]# 左边前缀和越小,子数组和越大,长度超短。如:47 取代了 84# q 是单调递增的,保证了左边右移,子数组的和越来越小,可找到最小窗口。while q and x <= acc[q[-1]]: q.pop() # 单调队列                while q and x - acc[q[0]] >= k: # 滑动窗口ans = min(ans, i - q.popleft())q.append(i) # 下标return ans if ans <= n else -1
class Solution {public int shortestSubarray(int[] nums, int k) {int n = nums.length;int ans = n + 1;long[] acc = new long[n + 1];Deque<Integer> q = new LinkedList();for (int i = 0; i < n; i++) acc[i + 1] = acc[i] + (long) nums[i];for (int i = 0; i < acc.length; i++){while (!q.isEmpty() && acc[i] <= acc[q.getLast()]) q.removeLast();while (!q.isEmpty() && acc[i] >= acc[q.getFirst()] + k) ans = Math.min(ans, i - q.removeFirst());q.addLast(i);}return ans <= n ? ans : -1; }
}

1438.绝对差不超过限制的最长连续子数组

from sortedcontainers import SortedListclass Solution:def longestSubarray(self, nums: List[int], limit: int) -> int:# 方法一、滑动窗口 有序列表s = SortedList()left = ans = 0for i, x in enumerate(nums):s.add(x)if s[-1] - s[0] > limit:s.remove(nums[left])left += 1return len(nums) - left# 方法二、滑动窗口 单调栈mx, mn = deque(), deque()left = 0for i, x in enumerate(nums):while mx and mx[-1] < x:mx.pop()while mn and mn[-1] > x:mn.pop()mx.append(x)mn.append(x)if mx[0] - mn[0] > limit:if mx[0] == nums[left]:mx.popleft()if mn[0] == nums[left]:mn.popleft()left += 1return len(nums) - left

1793.好子数组的最大分数

以每一个数作为最小值的作用范围,但必须包含 nums[k],所以超过 nums[k] 的数就不用考虑了。

class Solution:def maximumScore(self, nums: List[int], k: int) -> int:q, n = [], len(nums)ans = 0for i, x in enumerate(nums):heappush(q, (x, i))i, j = 0, n - 1    while q:x, idx = heappop(q)if idx > j or idx < i:continue # 区间以外            ans = max(ans, x*(j-i+1))if idx > k: j = idx - 1           elif idx < k: i = idx + 1else: break # 正好是 nums[k]return ans


其实只要排序的列表就可以了。

class Solution:def maximumScore(self, nums: List[int], k: int) -> int:ans, n, t = 0, len(nums), nums[k]# 大于 t 数就不用考虑了,因为用它作为最小值时一定不包含 tq = [(x, i) for i, x in enumerate(nums) if x <= t]q.sort(reverse=True)        i, j = 0, n - 1    while q:x, idx = q.pop()if idx > j or idx < i: continueans = max(ans, x*(j-i+1))if idx > k: j = idx - 1           elif idx < k: i = idx + 1else: breakreturn ans


以 nums[k] 为中心向两边扩展找更小的数的作用范围,直到 数组的最小值 作用范围是 整个数组,同时更新答案。

class Solution:def maximumScore(self, nums: List[int], k: int) -> int:        i, j, n = k, k, len(nums)ans = min_ = nums[k]        while i >= 0 or j < n:while i >= 0 and nums[i] >= min_: i -= 1while j < n  and nums[j] >= min_: j += 1# 当前 min_ 作用范围是 [i+1, j-1]ans = max(ans, min_*(j - i - 1))if j < n and i >= 0:min_ = max(nums[i], nums[j])else:min_ = nums[i] if j >= n else nums[j]return ans

*1157.子数组中占绝大多数的元素

1186.删除一次得到子数组最大和

class Solution:def maximumSum(self, arr: List[int]) -> int:       ans = a = b = -inf       for i, x in enumerate(arr):b = max(b + x, a) # 删除 xa = max(a + x, x) # 不删除 xans = max(ans, a, b)return ans

*1330.翻转子数组得到最大的数组值

*1460.通过翻转子数组使两个数组相等

*1526.形成目标数组的子数组最少增加次数

*1546.和为目标值且不重叠的非空子数组的最大数目

1567.乘积为正数的最长子数组长度

class Solution:def getMaxLen(self, nums: List[int]) -> int:        even, ans, fisrt, left = True, 0, -1, -1 # even 倜数for i, x in enumerate(nums):if x == 0:left, even, fisrt = i, True, -1 # 初始化continueif x < 0: if fisrt == -1:  fisrt = i # 初始化后负数第一次出现的位置even = not even            if even: ans = max(ans, i - left) # 偶数个负数else: ans = max(ans, i - fisrt)return ans

*1569.将子数组重新排序得到同一个二叉查找树的方案数

*1574.删除最短的子数组使剩余数组有序

class Solution:def findLengthOfShortestSubarray(self, arr: List[int]) -> int:n = len(arr)# 左边找第一个小于前一个的元素for left in range(1, n):if arr[left] < arr[left - 1]: breakelse: return 0# 右边找第一个大于后一个的元素for right in range(n - 2, -1, -1):if arr[right+1] < arr[right]: breakans = min(n - left, right + 1) i, j = 0, right + 1while i < left and j < n:if arr[i] <= arr[j]:ans = min(ans, j - i - 1);i += 1else: j += 1return ans

1630.等差子数组

class Solution:def checkArithmeticSubarrays(self, nums: List[int], l: List[int], r: List[int]) -> List[bool]:def check(a, b):            x = sorted(nums[a:b+1])diff = x[1] - x[0]return all(x[i] - x[i-1] == diff for i in range(2, len(x)))return [check(a, b) for a, b in zip(l, r)]

1764.通过连接另一个数组的子数组得到一个数组

class Solution:def canChoose(self, groups: List[List[int]], nums: List[int]) -> bool:n, i = len(nums), 0for group in groups:k = len(group)while i + k <= n:if group == nums[i: i + k]:i += k                    breakelse: i += 1else: return False            return True

2261.含最多K个可整除元素的子数组

class Solution:def countDistinct(self, nums: List[int], k: int, p: int) -> int:       s, n = set(), len(nums)for i in range(n):count = 0       for j in range(i, n):if nums[j] % p == 0: count += 1          if count > k: breaks.add(tuple(nums[i:j+1]))                  return len(s)

6248. 统计中位数为 K 的子数组

581.最短无序连续子数组

class Solution:def findUnsortedSubarray(self, nums: List[int]) -> int:q, n = [], len(nums)left, right = len(nums) - 1, 0mx, mn = nums[0], nums[-1]for i, x in enumerate(nums):'''# 利用单调栈找左边界while q and x < nums[q[-1]]:left = min(left, q.pop())q.append(i)'''# 逆序遍历,记录大于当前最小值的下标y = nums[-i -1]if y > mn: left = n-i-1else: mn = y# 记录小于当前最大值的下标if x < mx: right = ielse: mx = x           return 0 if right == 0 else right - left + 1i, j = 0, len(nums) - 1           x = sorted(nums)while x[i] == nums[i]:if i == j: return 0i += 1            while x[j] == nums[j]: j -= 1        return j - i + 1
class Solution {public int findUnsortedSubarray(int[] nums) {int n = nums.length, left = n - 1, right = 0, max = nums[0], min = nums[n - 1];for (int i = 0; i < n; i++){int x = nums[i], y = nums[n-i-1];if (y > min) left = n-i-1;else min = y;if (x < max) right = i;else max = x;}return right == 0 ? 0 : right - left + 1;}
}

1574.删除最短的子数组使剩余数组有序

class Solution:def findLengthOfShortestSubarray(self, arr: List[int]) -> int:n = len(arr)for i in range(n - 1):if arr[i] > arr[i + 1]: breakelse: return 0# i 有效,i + 1 无效for j in range(n - 1, 0, -1):if arr[j-1] > arr[j]: break# 整体删除右端,或左端ans = min(n - i - 1, j)k = 0 # 从第一个开始试着接后面的while k <= i and j < n:if arr[k] <= arr[j]: # 能接ans = min(ans, j - k - 1)k += 1else: j += 1return ans

子数组(Subarray)相关推荐

  1. C#LeetCode刷题之#581-最短无序连续子数组( Shortest Unsorted Continuous Subarray)

    问题 给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序. 你找到的子数组应是最短的,请输出它的长度. 输入: [2, 6, 4, 8, 10, ...

  2. [Swift]LeetCode978. 最长湍流子数组 | Longest Turbulent Subarray

    原文地址:https://www.cnblogs.com/strengthen/p/10294636.html A subarray A[i], A[i+1], ..., A[j] of A is s ...

  3. Minimum Size Subarray Sum 最短子数组之和

    题意 Given an array of n positive integers and a positive integer s, find the minimal length of a suba ...

  4. 【LeetCode】Maximum Product Subarray 求连续子数组使其乘积最大

    Add Date 2014-09-23 Maximum Product Subarray Find the contiguous subarray within an array (containin ...

  5. LeetCode209 长度最小的子数组(二分法)

    给定一个含有 n 个正整数的数组和一个正整数 target . 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, nums ...

  6. 常考数据结构与算法:子数组中的最大累加和问题

    题目描述 给定一个数组arr,返回子数组的最大累加和 例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子数组中,[3, 5, -2, 6]可以累加出最大的和12,所以返回12. ...

  7. 牛客题霸 [子数组的最大累加和问题] C++题解/答案

    牛客题霸 [子数组的最大累加和问题] C++题解/答案 题目描述 给定一个数组arr,返回子数组的最大累加和 例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子数组中,[3, ...

  8. 计数器数组_子数组计数

    计数器数组 Problem statement: 问题陈述: Given an array of N positive integers a1, a2,  ..., an. The value of ...

  9. 【算法07】求子数组的最大和

    题目:输入一个整型数组,数组里面有正数也有负数,数组中的连续一个或者多个整数组成一个子数组,每一个子数组都有一个和,求所有子数组和的最大值.要求时间复杂度为O(n). 例如:输入数组为{1,-2,3, ...

  10. [Swift]LeetCode1031. 两个非重叠子数组的最大和 | Maximum Sum of Two Non-Overlapping Subarrays...

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblog ...

最新文章

  1. 关于sybase数据库的锁
  2. 前端学习(1714):前端系列javascript之目录结构
  3. 改造MUC实现Openfire群
  4. jQuery选择器,用逗号分隔的时候需要注意范围问题
  5. rand函数怎么避免重复_Excel常用的计算统计函数
  6. swing tree 去掉双击默认展开 关闭_如何保护自己的电脑,关闭危险端口(一)
  7. in use 大学英语4word_《新视野大学英语4网络测试题unit6++Microsoft+Word+文档》.doc
  8. boost的编译和使用(window下)
  9. 图文并茂!CIC滤波器的FPGA实现
  10. UML入门以及Plant UML工具介绍
  11. 计算机不显示桌面文件夹,电脑保存文件路径不显示桌面怎么办
  12. ABAP BAPI 复制标准项目模板实现项目立项
  13. 如何把团队带成一盘散沙?
  14. 网页JS自动化脚本(一)安装油猴或暴力猴等脚本管理器并新建脚本
  15. userenv和sys_context函数
  16. 博易终于发布新版本了
  17. 给tensor增加维度 或 减少维度
  18. Android 注入 看雪
  19. 百度搜索简单使用 以及 搜书网址
  20. 东北电力大学计算机学院教务处,2020年东北电力大学计算机学院初试

热门文章

  1. 7-12 验证哥德巴赫猜想 (10 分)
  2. 华为手机日历倒计时_倒计时15天:麒麟芯片断供,华为手机普遍涨价,最高涨3000...
  3. 使用Stream操作List
  4. 看看电销外呼系统排行,选择哪家外呼公司靠谱?
  5. 优雅!用了这两款插件,我成了整个公司代码写得最规范的码农
  6. 青龙脚本(七猫免费小说,附脚本)
  7. 函数式编程与Lambda表达式
  8. 【Java】若依前后端分离,分页数据为null报错
  9. 合工大计算机考研导师,合肥工业大学计算机与信息学院导师介绍:胡东辉
  10. freescale S12X微控制器 模拟EEPROM 快速上手指南