跳到主要内容

3113. Max Boundary Subarrays

Find the Number of Subarrays Where Boundary Elements Are Maximum

乍看超难 通过率仅~34%34\%

实则定睛看 非常容易抓住核心秒解

题目名字太长起到的吓人作用👻

子数组的最大值 同时出现在左右尾

此乃本题要求我们找出来的子数组特性

我们由此能立马意识到 遍历nums时的注意事项:

遍历到nums[j]nums[j]

任何满足nums[i]<nums[j]nums[i] < nums[j]i<ji < jnums[i]nums[i]

jkj \leq knums[k]nums[k]围成的子数组nums[i:k+1]nums[i: k + 1]

都没办法满足题目要求 因为nums[i]<nums[j]nums[i] < nums[j]

担任左尾的nums[i]nums[i]确定不是此子数组最大值 没做到需求

因此 但凡nums[i]<nums[j]nums[i] < nums[j]i<ji < jnums[i]nums[i]

都可在遍历到nums[j]nums[j]时被划掉 永久淘汰

承上

把全部nums[i]<nums[j]nums[i] < nums[j]i<ji < jnums[i]nums[i]都划掉了

剩下的nums[i]nums[i] 要么等同nums[j]nums[j] 要么更大

于是东西变简单了~

I. 如果都没有和nums[j]nums[j]相等的nums[i]nums[i]

那么nums[j]nums[j]作为子数组右尾时

只有一个子数组满足题目要求 就是nums[j]nums[j]自己

II. 倘若有和nums[j]nums[j]相等的nums[i]nums[i]

我们仅需把nums[i]nums[i]对应的计数Countnums[i]Count_{nums[i]}加1

加1后的Countnums[i]Count_{nums[i]} 也是nums[j]nums[j]作为子数组右尾时

总共能吻合题目需求的可搭配左尾数

简述一下

第I和第II点的共性:都会包含nums[j]nums[j]自成子数组的情形

可是第II点还包含 早前和nums[j]nums[j]相等的数作左尾的情形

又双叒叕单调性

看到这边 大家已经意识到 这题的核心🫱单调性

靠严格递减单调栈做到前述的观察与策略

当遍历到nums[j]nums[j]时 只要栈不为空

且栈顶元素nums[i]<nums[j]nums[i] < nums[j] 就要弹栈顶

弹到最后 有可能栈顶是个 nums[j]nums[j]相等的nums[i]nums[i]

此时把Countnums[i]Count_{nums[i]}加1就好

反之则是直接让nums[j]nums[j]入栈 把Countnums[j]Count_{nums[j]}设为1

nums[j]nums[j]做右尾 能搭的左尾数 必是栈顶元素绑定的计数

需要注意的细节是 计数是和元素绑定的

因此弹栈顶时 自然把Countnums[i]Count_{nums[i]}给弹掉

剩下的自然是 每次算好Countnums[j]Count_{nums[j]}

nums[j]nums[j]带著计数入栈

Countnums[j]Count_{nums[j]}加入要回传的subarrays_count


def count_subarrays(nums: list[int]) -> int: # LeetCode Q.3113.
stack: list[tuple[int, int]] = [] # Format: (num, count).

subarrays_count = 0

for num in nums:
while stack and stack[-1][0] < num:
stack.pop(-1)

count = 1 # Base case: current num only.

if stack and stack[-1][0] == num:
count += stack.pop(-1)[1]

subarrays_count += count
stack.append((num, count))

return subarrays_count

Stack_Efficiency 时空复杂度就和典型的单调栈遍历题一样 双双O(n)O(n)

真的只要看穿遍历的淘汰机制 就能秒解啰~~

延伸问题

这道题目的答案subarrays_count 下限多少?上限多少?