https://leetcode-cn.com/problems/maximum-number-of-ways-to-partition-an-array/
思路:第一次在比赛中间干掉
h
a
r
d
hard
hard题,纪念一下。先计算出前缀和数组
s
u
m
sum
sum,并设整个数组的总和为
t
o
t
tot
tot,那么问题中的条件就等效于在前缀和数组中寻找
s
u
m
i
=
t
o
t
−
s
u
m
i
sum_i=tot-sum_{i}
sumi=tot−sumi的
i
i
i的个数,也即
s
u
m
i
=
t
o
t
/
2
sum_i=tot/2
sumi=tot/2,注意这里
i
i
i的取值范围是
[
0
,
n
−
1
)
[0,n-1)
[0,n−1)。我们仔细思考一下,修改一个数会产生哪些影响?不妨设把
n
u
m
s
i
nums_i
numsi改成
k
k
k,那么
s
u
m
[
0
…
i
)
sum[0…i)
sum[0…i)无变化,但是
s
u
m
[
i
,
…
n
)
sum[i,…n)
sum[i,…n)会加上
k
−
n
u
m
s
i
k-nums_i
k−numsi,且整个数组的总和
t
o
t
tot
tot也需要加上
k
−
n
u
m
s
i
k-nums_i
k−numsi。不妨设
d
i
s
=
k
−
n
u
m
s
i
dis=k-nums_i
dis=k−numsi,在经过这次变化之后,分割的方法数就等于
j
<
i
&
&
s
u
m
j
=
(
t
o
t
+
d
i
s
)
/
2
j<i\&\&sum_j=(tot+dis)/2
j<i&&sumj=(tot+dis)/2的个数加上
j
>
=
i
&
&
s
u
m
j
=
(
t
o
t
−
d
i
s
)
/
2
j>=i\&\&sum_j=(tot-dis)/2
j>=i&&sumj=(tot−dis)/2的个数。如果我们维护两个哈希表,分别记录
i
i
i左右两侧的前缀和数组的元素出现次数,那么就可以在
O
(
1
)
O(1)
O(1)复杂度内计算出分割方法数,整个算法的复杂度降低到了
O
(
n
)
O(n)
O(n)。
class Solution {
public:
int waysToPartition(vector<int>& nums, int k) {
int n=nums.size();
using ll=long long;
vector<ll> sum(n);
unordered_map<ll,int> leftCnt,rightCnt;
sum[0]=nums[0];
for(int i=1;i<n;i++)
{
sum[i]=sum[i-1]+nums[i];
++rightCnt[sum[i-1]];
}
int ans=0;
ll tot=sum[n-1];
if(!(tot&1)&&rightCnt.count(tot>>1))
ans=rightCnt[tot>>1];
for(int i=0;i<n;i++)
{
int dis=k-nums[i];
if(!((tot+dis)&1))
{
ll leftVal=(tot+dis)>>1;
ll rightVal=(tot-dis)>>1;
int curAns=0;
if(leftCnt.count(leftVal))
curAns+=leftCnt[leftVal];
if(rightCnt.count(rightVal))
curAns+=rightCnt[rightVal];
ans=max(ans,curAns);
}
--rightCnt[sum[i]];
++leftCnt[sum[i]];
}
return ans;
}
};