1. 题目描述
给定序列 {},输出序列中和为0的子段中,长度的最大值.
例如:{1, -1, 1},则和为0的最长子段长为 2.
2. 思路描述
若某子段的和为0,则 子段前的第一个元素的前缀和 一定等于 子段最后一个元素的前缀和
由图易知:L2 = L1 + L0,又 L0 = 0,则 L2 = L1. 在该情况下,该子段的长度等于两个前缀和相等的元素的下标之差,如 6 - 3 = 3.
这里考虑特殊情况:若 L1 的长度恰为0,即某个和为0的子段无排在其前面的元素,此时整个子段的长度就是子段末尾元素的下标值加1.
至此,问题转化为某个元素:
1. 若元素前缀和为0,则当前子段长为该元素的下标值 + 1.
2. 若元素前缀和不为0,是否存在排在其前面,且前缀和与其相等的元素,若存在,则子段长为两个前缀和相等的元素的下标之差,若不存在,则可以先记录当前元素前缀和及其下标,方便后续比较判断.
3. C++算法实现
遍历前缀和过程的三种情况
① 前缀和为0:整个子段为合法子串,无需将0记录 map 中
② 前缀和不为0,且从未出现,记录该前缀和下的下标,下标记录要加1
③ 前缀和不为0,且已经出现,则与所记录的下标之差为子段长
void Solution(vector<int>& a){
int res = 0; //默认无符合题意的子串
map<int,int> mp;
if(a[0] != 0)
mp[a[0]] = 1; //②
else
res = 1; //①
for(int i = 1; i < int(a.size()); i++){
a[i] += a[i-1];
if(a[i] == 0) //①
res = max(res,i + 1);
else if(mp[a[i]] == 0) //②
mp[a[i]] = i;
else{ //③
res = max(res,i - mp[a[i]]);
}
}
cout << res;
}
🚩 第2行:res为最终统计结果,初始状态下默认无和为0的子串.
🚩 第3行:利用STL的map实现前缀和的记录与查询,key 记录前缀和值,value记录下标.
🚩 第5~8行:为了方便前缀和统计,先记录a[0]的值. 若a[0]不为0,则需要记录mp中方便后序判断,否则直接以 0 + 1 作为符合的子段长度.
🚩 第11行:统计前缀和.
🚩 第14行:由于map默认情况下的 value 为0,故一次判断前面是否有相同前缀和的元素出现.
注意:相同前缀和元素下标只需记录第一次即可,因为这样得到的子段才会是最长子段.
4. 算法时间复杂度分析
针对每一个元素,统计前缀和消耗O(1)时间,map查询、插入消耗O(logN)时间,因此总时间复杂度为O(N·logN).