解题思路:必须以某个位置结尾的情况下,答案一定在其中。
1. 给定一个数组,有正有负,求累加和为target的最长子数组
解析:计算前缀和数组,
- sum表示从开始到 i 位置的累加和。
- Map:key指累加和,value指最早出现的位置。
public int maxLength(int[] arr, int k){
if(arr == null || arr.length == 0) return 0;
Map<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
int ret = 0; int sum = 0;
for(int i = 0; i < arr.length; i++){
sum += arr[i];
if(map.containsKey(sum - k))
ret = Math.max(ret, i - map.get(sum - k));
if(!map.containsKey(sum))
map.put(sum, i);
}
return ret;
}
2. 一个数组中,有奇数和偶数,求奇偶个数相同的最长子数组
解析:把奇数置为1, 偶数置为-1, 求累加和为0的最长数组
3. 一个数组中,含有0 1 2,求含有1和2数量相同的子数组有多少
解析:把2置为-1,求累加和为0的子数组
4. 给定一个数组,可任意把数组切分成不相容的子数组,使异或和为0的子数组最多,求最多的个数
解析:先求0~i 范围,再求 0 ~ i + 1范围
假设0 ~ i 数组存在最优化分,两个可能:
- i 所在子数组异或和不为0,则0 ~ i 和 0 ~ i - 1 最多划分的个数相同
- i 所在子数组异或和为0,则存在k一定是距离 i 最近的异或和为0的位置(若 0 ~ i 异或和为sum,则找出从 0 ~ i 之间为sum 的最晚的位置,即为pre)
dp[i] = math.max(dp[i - 1], dp[pre] + 1)
public int mostEOR(int[] arr){
int ans = 0;
int xor = 0;
int[] dp = new int[arr.length];
Map<Integer, Integer> map = new HashMap<>();
map.put(0, -1);
for(int i = 0; i < arr.length; i++){
xor ^= arr[i];
if(map.containsKey(xor)){
int pre = map.get(xor);
dp[i] = pre == -1 ? 1 : dp[pre] + 1;
}
if(i > 0)
dp[i] = Math.max(dp[i - 1], dp[i]);
map.put(xor, i);
ans = Math.max(ans, dp[i]);
}
return ans;
}
5. 给定一个数组,求子数组的最大异或和
注:若 E1^E2=E3 --> E1^E3=E2 ,E2^E3=E1
即:( 0到 i 的异或和)^(0到 j - 1 的异或和) == j 到 i 的异或和
解法 : 使用动态规划:dp记录 0 ~ i 的异或值
public int getMax(int[] arr){
int max = Integer.MIN_VALUE;
int eor = 0;
int[] dp = new int[arr.length];
for(int i = 0; i < arr.length; i++){
eor ^= arr[i];
max = Math.max(max, eor);
for(int j = 0; j < i; j++){
int curEor = eor ^ dp[j];
max = Math.max(max, curEor);
}
dp[i] = eor;
}
return max;
}