LeetCode 1712. 将数组分成三个子数组的方案数--二分+前缀和

  1. 将数组分成三个子数组的方案数

我们称一个分割整数数组的方案是 好的 ,当它满足:

数组被分成三个 非空 连续子数组,从左至右分别命名为 left , mid , right 。
left 中元素和小于等于 mid 中元素和,mid 中元素和小于等于 right 中元素和。

给你一个 非负 整数数组 nums ,请你返回 好的 分割 nums 方案数目。由于答案可能会很大,请你将结果对 109 + 7 取余后返回。

示例 1:

输入:nums = [1,1,1]
输出:1
解释:唯一一种好的分割方案是将 nums 分成 [1] [1] [1] 。

示例 2:

输入:nums = [1,2,2,2,5,0]
输出:3
解释:nums 总共有 3 种好的分割方案:
[1] [2] [2,2,5,0]
[1] [2,2] [2,5,0]
[1,2] [2,2] [5,0]

示例 3:

输入:nums = [3,2,1]
输出:0
解释:没有好的分割方案。

提示:

3 <= nums.length <= 105
0 <= nums[i] <= 104

题解

题目不难,先计算前缀和sum[x],那么我们可以从位置2开始暴力遍历每一个下标j,那么要满足题目要求的条件,就得是(n为数组长度):

sum[i]<=sum[j]-sum[i]<=sum[n]-sum[j]

因为计算前缀和这样我们计算快一些,我们暴力遍历j,然后去快速找到这样的一个i,那么答案+1,但是固定j不变,这样的i可能是有多个的,于是i有一个范围[i1,i2],满足sum[j]-sum[i2]>=sum[i2],sum[j]-sum[i1]<=sum[n]-sum[j],然后需要满足i1<=i2。

因为找到sum[j]-sum[i2]>=sum[i2],i2不断向左边移动一定继续满足这个条件。

AC代码

class Solution {
public:
    int sum[100010];
    int search(int L,int R,int target)
    {
        int l=L,r=R-1,fin=-1;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(sum[mid]<=sum[R]-sum[mid])
            {
                l=mid+1;
                fin=mid;
            }
            else r=mid-1;
        }
        int fin2=-1;
        l=L,r=R-1;
        while(l<=r)
        {
            int mid=(l+r)/2;
            if(sum[R]-sum[mid]<=target)
            {
                r=mid-1;
                fin2=mid;
            }
            else l=mid+1;
        }
        if(fin==-1||fin2==-1||fin2>fin)return 0;
        return fin-fin2+1;
    }
    int waysToSplit(vector<int>& nums) {
        sum[0]=nums[0];
        for(int i=1;i<nums.size();i++)
        sum[i]=sum[i-1]+nums[i];
        int res=0;
        for(int i=2;i<nums.size();i++)
        {
            res+=search(0,i-1,sum[nums.size()-1]-sum[i-1]);
            res%=1000000007;
        }
        return res;
    }
};

LeetCode 1712. 将数组分成三个子数组的方案数--二分+前缀和_第1张图片

你可能感兴趣的:(前缀和,二分查找,LeetCode,算法,leetcode,数据结构)