【算法】求数组中异或和为0的子数组最多有多少

求数组中异或和为0的子数组最多有多少

问题描述
假设一个数组 {3,2,1,0,1,2,3,0},求异或和为0的子数组最多有多少,也就是最多可以划分多少个?

答案:4

解析
数组 {3 2 1 0 1 2 3 0} 。可以划分为 {3,2,1} {0} {1,2,3} {0},四个子数组异或和都为0。也可以划分为 {3},{2},{1},{0},{1},{2},{3},{0},这样子,子数组中异或和为0的子数组就只有{0},一共2个。

异或的性质

  1. 异或运算满足交换律和结合律 a^b = b^a ; 如果 a ^ b ^ c ^ d = 0,a^b = 0, 那么c^d = 0
  2. n ^ 0 = n
  3. n ^ n = 0

算法思想
0-i的范围能求出来最多的异或和为0的子数组。0-i上此时是最优划分

  1. i所在的最后一个部分不是异或和为0的子数组
    {0 , i-1}上能划分出来的最多子数组就等于 {0 - i}上最多子数组,也就是要不要它无所谓。dp[i] = dp[i-1];
  2. i所在的最后一部分是异或和为0的子数组
    那么接下来,i作为最优划分的部分,也就是
    在这里插入图片描述

也就是我们要找到最后一个k出现的位置让[k+1,i]的异或和为0。这样子,就能使异或和为0的子数组最多。
k也能在j的位置,所以我们要借助哈希表通过找到最后一个k的位置,来求出答案。也就是dp[i] = dp[k ] + 1.

怎么用哈希表将此功能实现呢
我们要将所有的,从0到1,从0到2,从0到n-1的异或和记录在map中,并且记录他们出现的最后一次的位置。为什么要这么做呢?如上图 0-j的异或和为0,j-k的异或和为0,k到i的异或和为0。所以0-i的异或和也是0,j到i的异或和也是0。我们偏偏需要k的位置,就是因为我们将异或和为0的位置用k覆盖了,所以我么才能找到k的位置。来进行接下来一系列操作,dp数组的记录。

dp数组的功能
dp[i]表示从 0-i这个区间最多可以由几个子数组来构成。 因为异或的性质,假如v{0,1,2,3}。变量xor一开始为0, xor^= v[0] = 0, dp[0] = 1记录下来。
xor^=v[1] !=0, dp[1]=max(dp[1],dp[0]) = dp[0]。表明的意思是数组v中的[0,1]这个构成的数组中的子数组最多可以有1个,那就是{0}(符合),{1}。
xor^=v[2] !=0, dp[2]=max(dp[2],dp[1]) = dp[1]。表明的意思是数组v中的[0,1,2]这个构成的数组中的子数组最多可以有1个,那就是{0}(符合),{1,2}
直到xor^= v[3] = 0。所以dp[3] = dp[2]+1 = 2。表明的意思是数组v中的[0,1,2]这个构成的数组中的子数组最多可以有2个,那就是{0}(符合),{1,2,3}(符合)

代码实现

int mostEOR(vector<int>& arr)
{
	int ans = 0;
	int xor = 0;
	vector<int> dp(arr.size());
	map<int, int> mymap;
	mymap[0] = -1; // 如果异或和为0的话,prev就为-1,标记异或和为0的子数组是否出现过
	for (int i = 0; i < arr.size(); i++)
	{
		xor ^= arr[i];
		if (mymap.find(xor) != mymap.end())
		{
			int pre = mymap[xor];  // 如果xor为-1,说明此时0-i的异或和为0,dp就需要记录
			dp[i] = (pre == -1 ? 1 : dp[pre] + 1);
		}
		if (i > 0)
		{
			dp[i] = max(dp[i - 1], dp[i]);
		}
		mymap[xor] = i;
		ans = max(ans, dp[i]);
	}
	return ans;
}

猜你喜欢

转载自blog.csdn.net/weixin_43939593/article/details/105842133