1. 题目来源
链接:525. 连续数组
2. 题目解析
前缀和变种,思维型题目。
在此 s[i]
定义为,前 i
个数中,1 的个数减去 0 的个数的差值。在此定义下,[j,i]
区间的 1 的个数减去 0 的个数的差值就是 s[i]-s[j-1]
。是满足前缀和的推导公式的,也可以看作是容斥原理。
那么针对 i
位置,以 i
位置为终点,j
为起点,若能构成答案,则必然有 s[i]-s[j-1]=0
即 1 的个数与 0 的个数相等,即 s[i]=s[j-1]
。
故相当于统计 i
位置之前是否存在与 s[i]
相等的位置即可。且在此需要求的是区间长度,故哈希表统计两个值 :s[j],j
,首先在哈希表中 hs[0]=0
,作为边界。
顺序枚举 i
,如果哈希表中存在 s[i]
值相等的话,就是满足条件的答案区间,可以更新答案。如果不存在 s[i]
值相等的话,说明 s[i]
是第一次出现,对应的 i
位置也是第一次出现的最小下标,故更新 hs[s[i]] = i
。
知识理解:
注意前缀和需要深刻理解下标这个概念,[j, i]
区间 1 和 0 的差值是 s[i]-s[j-1]
。再进一步,[i,i]
这个区间,其长度是 1,其 1 和 0 的差值是 s[i]-s[i-1]
。
我们在下标存储的时候是针对 s[i]
存的 i
下标,那么针对 s[i-1]
就存的是 i-1
这个下标。对应的 s[j-1]
存的就是 j-1
这个下标。
那么当我们找到和 s[i]
相等的值的时候,计算区间长度,按理说是左端点减右端点再 +1 的,但是针对前缀和这种特殊的问题来讲,若 s[i]=s[j]
其实是 [j+1,i]
区间是答案,故可以直接进行 i-j
就等价于 i-(j+1)+1
作为区间长度了。
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
class Solution {
public:
int findMaxLength(vector<int>& nums) {
int n = nums.size();
unordered_map<int, int> hs;
hs[0] = 0;
int res = 0;
for (int i = 1, one = 0, zero = 0; i <= n; i ++ ) {
int x = nums[i - 1];
if (x) one ++ ;
else zero ++ ;
int s = one - zero;
if (hs.count(s)) res = max(res, i - hs[s]); // 前缀和求区间长度不需要 +1
else hs[s] = i; // 没有出现过,是最小下标
}
return res;
}
};