题解:
这道题考试时真不会做,只想到了前缀和,并没有想到双指针,麻了,我是废物。
思路:双指针
由于数组元素是非负的,所以区间和具有单调性,所以我们可以枚举两个分界点。首先我们可以先固定第二个分界点 i,来寻找第一个分界点 x 的上下界 [j,k]
,那么在x的上下界满足left<=mid;mid<=right
情况下,此次的方案数为k-j+1
。
关于 i,j,k 的初始化讲解:i=3 表示 i 前面最少有两段,j=2 k=2 表示第一个边界从第二个点开始扩展,剩下的讲解在代码里面。
代码如下:
const int mod = 1e9+7;
class Solution {
public:
// 前缀和+双指针
// 思路:由于求的是连续子区间的和,所以用前缀和;
int waysToSplit(vector<int>& nums) {
int n=nums.size();
vector<int> pre(n+1,0);
// 前缀和预处理:pre[i]表示前i项的和,pre[1]为前1项的和,pre[n]为前n项的和
for(int i=1;i<=n;++i)pre[i]=pre[i-1]+nums[i-1];
int res=0;
/* 为何j,k不需要每次循环都重置呢?
因为当i一直枚举到n时,也就是i一直向右走时,[j,i-1]变大,[i,n]变小,那样mid<=right就不会成立,j自然也就右移了,不需要进行重置
同理k也是如此,[k.i-1]会变大,[1,k-1]不变,我们将k继续右移来寻找left<=mid更大的边界k,也不要重置
*/
for(int i=3,j=2,k=2;i<=n;++i)
{
// 当 right<mid,我们的j需要右移来使mid减小,来让right>=mid,此时我们找到就是使mid<=right的最小位置j
while(pre[n]-pre[i-1]<pre[i-1]-pre[j-1])j++;
// 我们的k尝试向前探测,当k取k+1满足mid>=left时,k可以向右走一步,我们就是这样来寻找上界的
while(k+1<i&&pre[i-1]-pre[k]>=pre[k])k++;
// 找到第一个分界点的上下界[j,k]后,我们需要判断 left<=mid mid<=right 是否成立来更新res
if(j<=k&&pre[n]-pre[i-1]>=pre[i-1]-pre[j-1]&&pre[i-1]-pre[k-1]>=pre[k-1])
res=(res+k-j+1)%mod;
}
return res;
}
};