0%

Leetcode 3721. 最长平衡子数组 II(最大后缀和,最小后缀和线段树)

题目大意

给你一个整数数组 nums

如果子数组中 不同偶数 的数量等于 不同奇数 的数量,则称该 子数组 是 平衡的 。

返回 最长 平衡子数组的长度。

子数组 是数组中连续且 非空 的一段元素序列。

  • 1 <= nums.length <= 10^5

  • 1 <= nums[i] <= 10^5

思路讲解

https://leetcode.cn/problems/longest-balanced-substring-ii/description/?envType=daily-question&envId=2026-02-13

其实这种平衡子串类似的问题,都是枚举右维护左。就是我们先确定了结尾,然后要去比较快速的找到开头,那么像这道,上面这道题目也是一样的。

这道题目维护左是采用的是线段数二分,使用线段数二分,在知道右边的情况下去找到左边。这该线段树需要维护区间,其实就是该线段树需要维护最大子段和最小子段和。该线段树需要能够支持查询区间最大子段和和最小子段和。

不过我们仔细细想以后发现,应该这道题目维护的是最大后缀和最小后缀和。实际上我们使用小白逛公园的线段树也可以维护最大后缀和和最小后缀和。

为什么要维护最大子段和和最小子段和呢?它实际上是这样子,它首先是把奇数变成一,偶数变成负一。当然都是最右边的那个值啊,奇数变成一,偶数变成负一。然后如果说我们知道了这一段的最大子段和是多少,最小子段和是多少,那么其实这一段的子段和是否有零?

image

显然,暴力解法无法满足时间要求,我们需要找到一个时间复杂度较低的方法来快速找到最长平衡子数组,于是我想到了线段树,通过区间修改和快速查询,可以实现时间复杂度O(N log N)。

那么如何用线段树快速找到最长平衡子数组呢,我们可以规定,如果数字是 偶数+1,反之,如果是 奇数-1。那这个问题就转换为,我们要找到 区间和 为0 的最长子区间长度。

但题目还有一个数字去重的要求,如果数字相同,则不能重复计算贡献,那我们可以这么想:

image

换句话说,我更新区间和的时候,只从 j 点更新到上一次这个数字出现前的位置(也就是图中1的位置),再往后这个数字就不能贡献计数了。

image

好了,线段树构造好了,那如何快速找到 区间和 为 0 的 子区间呢?由于这个线段树没有单调性,如果用朴素的方法,我们可能需要遍历整棵树,复杂度来到了O(N),不符合要求,解决方法是,采取Min/Max 剪枝策略,具体如下:在每个节点除了记录区间和,还要记录区间最大和 Max 和区间最小和 Min,并在寻找 区间和为 0 的时候做以下判断:

image

这样,就像走迷宫有了导航,复杂度可以下降到 O(Log N) :

image

以下为具体线段树更新和查找步骤图示:

image

image

image

image

image

回顾一下线段树算法的核心流程:

image

AC代码

心路历程(WA,TLE,MLE……)