求和为aim的最长子数组长度(也可以拓展为,求奇数个数和偶数个数相同的最长子数组长度)

给定一个数组,有正、有负、有零;还有一个aim值
求累加和为aim值的最长子数组。

例子:
如下数组,有很多子数组
在这里插入图片描述
子数组 有 各自的累加和
如果aim为7,则最长子数组为:
在这里插入图片描述
返回长度为4。

**规律:**对于数组 [i,……,k,k+1,……,j] ,如果要求aim为800,而我们知道从 i 累加到 j 的累加和为2000,那么从 i 开始向后累加,如果累加到 k 时累加和才达到1200,那么 k+1~j 就是整个数组中累加和为800的最长子数组。

**步骤:**以 [7,3,2,1,1,7,-6,-3,7] 、 aim=7 为例,

1.首先将 (0,-1) 放入 HashMap 中,代表0这个累加和在还没有遍历时就出现了。 ->(0,-1)

2.接着每遍历一个数 就将该位置形成的累加和 存入 HashMap ,比如 arr[0]=7 ,0位置上形成的累加和 为前一个位置形成的累加和0 加上本位置上的 7,因此将 (7,0) 放入 HashMap 中 表示0位置上第一次形成累加和为 7 ,然后将该位置上的累加和 减去 aim ,即 7-7=0 ,找第一次形成累加和为0的位置,即 -1 ,因此以下标为0结尾的子数组中和为aim的最长子数组为 0~0 ,即 7 一个元素,记最大长度 maxLength=1 。 ->(7,0)表示 累加和为7,最早出现在0位置。

3.接着来到 arr[1]=3 ,1位置上形成的累加和为 7+3=10 , HashMap 中没有 key 为 10 的记录,因此放入(10,1) 表示1位置上最早形成累加和为10,然后将该位置上的累加和减去 aim 即 10-7=3 ,到 HashMap 中找有没有 key 为 3 的记录(有没有哪个位置最早形成累加和为3),发现没有,因此以下标为1结尾的子数组中没有累加和为 aim 的。 ->(10,1) 表示 累加和为10,最早出现在1位置。

4.接着来到 arr[2]=2 ,2位置上形成的累加和为 10+2=12 , HashMap 中没有 key 为 12 的记录,因此放入(12,2) , sum-aim=12-7=5 ,到 HashMap 中找有没有 key 为 5 的记录,发现没有,因此以下标为2结尾的子数组中没有累加和为 aim 的。 ->(12,2)
5.来到 arr[3]=1 ,放入 (13,3) , sum-aim=5 ,以下标为3结尾的子数组没有累加和为aim的。 ->(13,3)

6.来到 arr[4]=1 ,放入 (14,4) , sum-aim=7 ,发现 HashMap 中有 key=7 的记录 (7,0) ,即在0位置上累加和就能达到7了因此 1~4 是以下标为4结尾的子数组中累积和为 7 的最长子数组,更新maxLength=4 。 ->(14,4)

7.来到 arr[5]=7 ,放入 (21,5) , sum-aim=14 , HashMap 中有 (14,4) ,因此 5~5 是本轮的最长子数组,但 maxLength=4>1 ,因此不更新。 ->(21,5)

8.来到 arr[6]=-6 ,放入 15,6 ,没有符合的子数组。 ->(15,6)

9.来到 arr[7]=-1 ,累加和为 15+(-1)=14 ,但 HashMap 中有 key=14 的记录,因此不放入 (14,7)
( HashMap 中保存的是某累加和第一次出现的位置,而14这个了累加和最早在4下标上就出现了)。 sumaim=7 , HashMap 中有 (7,0) ,因此本轮最长子数组为 1~7 ,因此更新 maxLength=7 。

9.来到 arr[8]=7 ,累加和为21,存在key为21的记录,因此不放入(21,7)。 sum-aim=14 ,本轮最长子数组为 5~8 ,长度为4,不更新 maxLength 。

代码:
len为全局最大值
在这里插入图片描述
先准备一个哈希map,将0 -1放入
在这里插入图片描述
sum不断加当前数,然后判断sum-aim是否出现过,
比较并更新全局最大值,
如果sum在map中没有出现过,则加入!
在这里插入图片描述
在这里插入图片描述

进阶:

一个数组可以有很多种切的方式,
求任意划分数组的方案中,划分后,异或和为0的子数组最多有多少个。
异或和:所有的数 全异或,和为0。
规律:异或运算符合交换律和结合律。
0和任何数异或,都是那个数:0^N=N ,
N 和自己异或,结果一定是0:N^ N=0 。
一坨数 异或的结果 和 异或的先后顺序 无关。

这样切,只有两个子数组异或和为0。
在这里插入图片描述
这样切,没有一个子数组异或和为0。
在这里插入图片描述
这样能切出4个!
在这里插入图片描述

可能性分析:

对于一个数组 [i,……,j,m,……,n,k] ,假设进行符合题意的最优划分后形成多个子数组后,k作为整个数组的末尾元素必定也是最后一个子数组的末尾元素。
最后一个子数组只会有两种情况:异或和不为0、异或和为0。

如果是前者,那么最后一个子数组即使去掉k这个元素,其异或和也不会为0,
如果是后者,最优划分会将最后一个子数组划分为两个子数组,其中k单独为一个子数组。

比如最后一个子数组是 indexOf(m)~indexOf(k) ,其异或和不为0,那么 dp[indexOf(k)]=dp[indexOf(k)-1] ,
表示数组 0~indexOf(k) 的解和其子数组 0~(indexOf(k)-1) 的解是一样的。 ->case 1

如果是后者,那么最后一个子数组中 不可能存在 以k为结尾的 更小的异或和为0 的子数组。比如最后一个子数组是 indexOf(m)~indexOf(k) ,其异或和为0,那么 dp[indexOf(k)]=dp[indexOf(m)-1]+1 ,

dp[k-1]表示0到k-1位置上的最优划分。
dp在两种决策中选最大的!
在这里插入图片描述

表示数组0~indexOf(k) 的解=子数组 0~(indexOf(m)-1) 的解+1。 ->case 2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值