线性动态规划:最长湍流子数组的变种——最多允许翻转一个子数组的最长湍流子数组
题目描述
给定一个整数数组 arr,如果比较符号在子数组的每个相邻元素对之间翻转(即 > 和 < 交替出现),则该子数组称为湍流子数组。例如,[4, 2, 10, 7] 是湍流的,因为 4 > 2、2 < 10、10 > 7。现在允许你最多翻转数组中的一个连续子数组(翻转指反转子数组的顺序),然后找到翻转后可能的最长湍流子数组的长度。
示例
输入:arr = [9, 4, 2, 10, 7, 8, 8, 1, 9]
输出:6
解释:翻转子数组 [8, 8](实际上翻转后仍是 [8, 8],但允许操作),可以得到湍流子数组 [4, 2, 10, 7, 8, 1] 或 [2, 10, 7, 8, 1, 9],长度为 6。
解题过程
步骤 1:理解基础湍流子数组问题
首先,回顾经典湍流子数组问题(无需翻转):定义 up[i] 为以 i 结尾且最后一段是上升(arr[i] > arr[i-1])的最长湍流子数组长度;down[i] 为以 i 结尾且最后一段是下降(arr[i] < arr[i-1])的长度。转移方程为:
- 若
arr[i] > arr[i-1]:up[i] = down[i-1] + 1,down[i] = 1 - 若
arr[i] < arr[i-1]:down[i] = up[i-1] + 1,up[i] = 1 - 若相等:
up[i] = down[i] = 1
最终答案是所有max(up[i], down[i])的最大值。
步骤 2:分析翻转操作的影响
翻转一个连续子数组相当于将原数组分成三部分:[左段, 中段, 右段],翻转中段后,中段内部符号顺序反转,同时中段与左段的连接处、中段与右段的连接处的符号关系可能改变。目标是最大化翻转后的全局湍流子数组长度。
关键观察:
- 翻转操作最多只能修复一个“断点”(即不满足湍流条件的位置)。
- 最优解通常是通过翻转一个短子数组(甚至长度为 0 或 1)来连接左右两个湍流段。
步骤 3:定义状态和预处理
我们预处理以下数组:
left_up[i]:从i向左的最长湍流子数组长度,且以i结尾是上升(即arr[i] > arr[i-1])。left_down[i]:从i向左的最长湍流子数组长度,且以i结尾是下降。right_up[i]:从i向右的最长湍流子数组长度,且以i开头是上升。right_down[i]:从i向右的最长湍流子数组长度,且以i开头是下降。
这些数组可以通过左右各扫描一次得到,类似经典问题但方向不同。
步骤 4:枚举翻转中段的左右端点
直接枚举所有子数组翻转会超时(O(n²))。优化:考虑翻转中段长度为 k,则中段翻转后,其内部符号反转,但湍流性质保留(因为符号交替性在反转后依然保持)。因此,翻转中段本身不影响内部湍流长度,关键在于连接左右段。
更优策略:枚举连接点。对于每个位置 i,检查以下情况:
- 不翻转:直接取
max(left_up[i], left_down[i])。 - 翻转一个子数组来连接
i左右的湍流段:- 情况 A:翻转
[i, i+1](即交换arr[i]和arr[i+1]),检查是否能连接左右段。 - 情况 B:翻转
[i-1, i],类似。 - 情况 C:翻转更长的子数组,但实际最优解通常只需翻转 2 个元素或更短。
- 情况 A:翻转
实际上,只需考虑翻转长度为 0(不翻转)、1(无意义)、2、3 的情况,因为更长翻转可以分解为短翻转的组合。
步骤 5:具体枚举情况
我们枚举每个索引 i 作为潜在连接点,计算以下可能:
- 不翻转:答案至少为
max(left_up[i], left_down[i])。 - 翻转两个元素(如
[i, i+1]):
翻转后,arr[i]和arr[i+1]交换,检查arr[i-1]与arr[i+1]的关系、arr[i]与arr[i+2]的关系是否能连接左右湍流段。
例如,若left_down[i-1]表示以i-1结尾是下降,则要求arr[i-1] > arr[i+1]才能连接;同时右段要求arr[i] < arr[i+2]才能接上right_up[i+2]。
连接后长度 =left_down[i-1] + 2 + right_up[i+2]。 - 翻转三个元素(如
[i-1, i+1]):类似,但中段长度为 3,翻转后中段内部符号反转,连接处检查arr[i-2]与arr[i+1]、arr[i-1]与arr[i+2]的关系。
步骤 6:算法实现概要
- 预处理
left_up,left_down,right_up,right_down。 - 初始化答案
ans为不翻转时的最大湍流子数组长度。 - 对于每个
i从 1 到 n-2:- 检查翻转
[i, i+1]:- 若
arr[i-1] > arr[i+1]且与left_down[i-1]兼容,则可能连接左段。 - 若
arr[i] < arr[i+2]且与right_up[i+2]兼容,则连接右段。 - 更新
ans。
- 若
- 检查翻转
[i-1, i]:类似。 - 检查翻转
[i-1, i+1]:类似但考虑中段长度 3。
- 检查翻转
- 注意边界处理(索引不越界)。
步骤 7:复杂度分析
预处理 O(n),枚举 O(n),总复杂度 O(n),空间 O(n)。
示例验证
对 arr = [9,4,2,10,7,8,8,1,9]:
- 不翻转时最长湍流子数组为 5(如
[4,2,10,7,8])。 - 翻转
[8,8](索引 6~7)后,可连接[2,10,7]和[1,9],得到长度 6。
算法会枚举到连接点i=7左右,通过翻转短子数组得到更长结果。
通过以上步骤,即可求出最多翻转一个子数组后的最长湍流子数组长度。