Problem Statement

Given an array AA of length NN and an integer KK, find minimum number of elements to change in the array such that xor of all segments of size KK becomes zero.

where 1KN20001 \leq K \leq N \leq 2000 and 0Ai10240 \leq A_i \leq 1024

As the window of size KK moves one step from first KK elements it leaves behind the first element and includes K+1thK+1_{th} element. More formally

A1A2A3AK=A2A3A4AK+1 A_1 \oplus A_2 \oplus A_3 \oplus \cdots \oplus A_K = A_2 \oplus A_3 \oplus A_4 \oplus \cdots \oplus A_{K+1}

Therefore, all numbers which are distance KK apart in the array must be equal.

Ai+bK=const A_{i+bK} = const

So we can make groups of numbers and operate on those groups, as to get the end result every number in a group must be same. If a number is at position ii then it belongs to the group categorized by i%Ki \% K.

As an example if K=2K = 2 then there will be two buckets to categorize all the numbers in. All numbers at odd positions in the array will belong to bucket 11 and even positioned numbers will fall in bucket 00.

At this step we have positions grouped together according to the remainder they give when divided by KK, now we have to assign a number to every group such that xor of every subarray of length KK is zero.


What will be the cost of assigning number XX to all the positions in a group GG?

If total positions in group GG are tot[G]tot[G] and cnt[G][X]cnt[G][X] is count of XX in group GG. Then cost of assigning XX to group GG is equal to the numbers which are not equal to XX in group GG, given by tot[G]cnt[G][X]tot[G]-cnt[G][X]


If you write a naïve solution that tries every number in the range [0,1024][0, 1024] for every group and then checks if condition is met at the end. Then it will not scale for more than 2 groups i.e. K>2K>2 as it is of order of 1024K1024^K. Even sample cases have K>2K>2 for two cases.


Slower DP solution

Let dp[i][xr]dp[i][xr] be the minimum changes to make in first ii groups such that xor of every subarray of length i+1i+1 is xrxr not considering the numbers in groups i+1i+1 to k1k-1.

If we are considering a number numnum to be the value of group ii then

dp[i][xr]=min( dp[i][xr] , dp[i1][xrnum] + cost of assigning num)dp[i][xr] = min ( \space dp[i][xr] \space, \space dp[i-1][xr \oplus num] \space + \space cost \space of \space assigning \space num)

or, dp[i][xr]=min( dp[i][xr] , dp[i1][xrnum] + tot[i] cnt[i][num]) dp[i][xr] = min(\space dp[i][xr] \space, \space dp[i-1][xr \oplus num] \space + \space tot[i] - \space cnt[i][num])

Now we can try every possible number for every group in a faster way and the final answer will be dp[K1][0]dp[K-1][0]

for(int i=0;i<K;i++){
	for(int xr=0;xr<1024;xr++){
		for(int num=0;num<1024;num++){
			if(i==0)
				dp[i][xr]=tot[i]-cnt[i][xr];
			else
				dp[i][xr]=min(dp[i][xr], dp[i-1][xr^num]+tot[i]-cnt[i][num]);
		}
	}
}

cout<<dp[K-1][0]<<"\n";

Why is this slow ? It’s complexity is O(K220)O(K*2^{20}) which won’t pass if K500K \geq 500 or some equivalent metric.


Faster DP solution

One observation is cnt[G][num]=0cnt[G][num] = 0 for some number numnum then its cost is tot[G]tot[G]. Therefore we can modify the innermost loop to check only for the numbers that exist in a particular group so it runs NK\lceil \frac{N}K \rceil times for every xrxr , and check for all other numbers in constant time.

It brings down the complexity to O(N1024)O(N*1024) as every existent position will be used once for every value of xrxr.

Another way to understand is that outer loop runs KK times, inner loop 10241024 times and innermost loop runs for NK\lceil \frac{N}K \rceil times.

How to calculate dp for all the numbers that are not present in the array?

Only task remaining is to take into account all the numbers that are not present in a group, and calculation for all of them can be done at once.

Key thing to notice is that for every xrxr we should consider only one such number that is not present in the group, as it doesn’t affect the cost which is always tot[G]tot[G], but allows us to choose the minimum value for dp[i1][xrnum]dp[i-1][xr \oplus num].

As we don’t know what numnum is beforehand (and we don’t need to), just take the minimum value of dp[i1][for any xr]dp[i-1][for \space any \space xr] and call it lastMinimumlastMinimum.

So, best value of dp[i][xr]dp[i][xr] for all other numbers not in the group will be

lastMinimum+tot[G]lastMinimum + tot[G]

In this code group[i]group[i] is the set of numbers in group ii.

int lastMinimum=0,tillnow=K;
for(int i=0;i<K;i++){
	tillnow=n; //to update lastminimum

	for(int xr=0;xr<1024;xr++){

		for(int num:group[i]){
			if(i==0)
				dp[i][xr]=tot[i]-cnt[i][xr];
			else
				dp[i][xr]=min(dp[i][xr],dp[i-1][xr^num]+tot[i]-cnt[i][xr]);

		}

		//for all numbers that are not in current group
		dp[i][xr]=min(dp[i][xr],lastMinimum+tot[i]);
		
		tillnow=min(tillnow,dp[i][xr]);
	}
	
	lastMinimum=tillnow;
}

cout<<dp[K-1][0]<<"\n";

You can practice this problem here on leetcode.

Similar problems