1. 题目来源
2. 题目解析
十分经典的双指针应用,算是比较难的双指针应用了,边界情况较多。其实完全也可以二分来做,但是双指针可以直接达到 O ( n ) O(n) O(n),是最优的做法,且具有很强的可推广性。
思路:
- 预处理前缀和,由于元素均非负,则前缀和数组具有单调性。
- 枚举
i
,在1~i-1
中枚举合法的x
的个数即可找到方案数。 - 这样会将整个数组分为三段,即:
1~x-1
,x~i-1
,i~n
。在此由于说的是前缀和数组,则下标从 1 开始的。 - 可发现由于
i
的向右移动,x
的左边界j
和右边界k
只会向右移动。具体证明可参考上面的笔记。 - 故双指针的单调性质就找到了。
细节:
-
i
要从 3 开始,这三段都必须有元素,不可为空。 -
找
j
指针的时候是看当前不行了,再去看下一个行不行,尽量少的向右走。 -
找
k
指针的时候是看下一个行不行,如果下一个k+1
满足了,则k
就往右边走一个到k+1
的位置,再看下一个位置行不行,尽量多的向右走。 -
关于前缀和求区间和的方式注意下标的使用。注意这三段区间的划分配合前缀和的使用。
-
时间复杂度: O ( n ) O(n) O(n)。
-
空间复杂度: O ( n ) O(n) O(n)
代码:
class Solution {
public:
int waysToSplit(vector<int>& nums) {
int n = nums.size(), mod = 1e9+7;
vector<int> s(n + 1);
int res =0;
for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + nums[i - 1];
for (int i = 3, j = 2, k = 2; i <= n; i ++ ) {
while (s[n] - s[i - 1] < s[i - 1] - s[j - 1]) j ++ ; // 不满足往后走
while (k + 1 < i && s[i - 1] - s[k] >= s[k]) k ++ ; // 试探下一个k是否满足,k-1和+1抵消了
if (j <= k && s[n] - s[i - 1] >= s[i - 1] - s[j - 1] && s[i - 1] - s[k - 1] >= s[k - 1])
res = (res + k - j + 1) % mod;
}
return res;
}
};