题目描述
给定一个长度为 n 的环形整数数组 nums,环形数组意味着数组的末端将会与开头相连呈环状。找出这个环形数组的非空连续子数组的最大和。
子数组:必须是连续的,环形代表子数组可以跨数组首尾,例如数组
[5,-3,5],可以选取[5,5](第一个 5 和最后一个 5)。
核心思路
环形子数组最大和只有两种情况:
- 不跨首尾 :普通最大子数组和(就是 LeetCode53 最大子序和),记为
ret1 - 跨首尾 :子数组包含数组头部 + 尾部,等价于总和减去中间一段最小子数组和 ,
sum - ret2,ret2是最小子数组和
⚠️ 边界特例:数组全部都是负数 此时总和减去最小子数组,会得到空数组(不合法,题目要求子数组非空),这种情况直接返回普通最大子数组(也就是数组里最大的单个负数)。

状态定义
fx[i]:以i下标结尾的最大子数组和gx[i]:以i下标结尾的最小子数组和
状态转移:

遍历过程中:
ret1:记录全局最大子数组和ret2:记录全局最小子数组和sum:整个数组总和flag:标记数组是否存在正数,用来判断是否全负
cpp
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int n=nums.size();
vector<int>fx(n,0);
auto gx=fx;
fx[0]=nums[0];
gx[0]=nums[0];
int ret1=nums[0];
int ret2=nums[0];
int sum=nums[0];
int flag=0;
for(int i=1;i<n;i++)
{
fx[i]=max(fx[i-1]+nums[i],nums[i]);
gx[i]=min(gx[i-1]+nums[i],nums[i]);
ret1=ret1>fx[i]?ret1:fx[i];
ret2=ret2<gx[i]?ret2:gx[i];
sum+=nums[i];
if(nums[i]>0)
flag=1;
}
if(flag==0)
return ret1;
return max(ret1,sum-ret2);
}
};