https://leetcode-cn.com/problems/make-the-xor-of-all-segments-equal-to-zero/
思路:依据题意,有:
n
u
m
s
[
i
]
x
o
r
n
u
m
s
[
i
+
1
]
…
…
x
o
r
n
u
m
s
[
i
+
k
−
1
]
=
0
(
1
)
n
u
m
s
[
i
+
1
]
x
o
r
n
u
m
s
[
i
+
2
]
…
…
x
o
r
n
u
m
s
[
i
+
k
]
=
0
(
2
)
nums[i]\ xor\ nums[i+1]……xor\ nums[i+k-1]=0 \ \ \ (1)\\ nums[i+1]\ xor\ nums[i+2]……xor\ nums[i+k]=0\ \ \ (2)
nums[i] xor nums[i+1]……xor nums[i+k−1]=0 (1)nums[i+1] xor nums[i+2]……xor nums[i+k]=0 (2)
(
1
)
、
(
2
)
(1)、(2)
(1)、(2)异或可得:
n
u
m
s
[
i
]
x
o
r
n
u
m
s
[
i
+
k
]
=
0
nums[i]\ xor\ nums[i+k]=0
nums[i] xor nums[i+k]=0,即
n
u
m
s
[
i
]
=
n
u
m
s
[
i
+
k
]
nums[i]=nums[i+k]
nums[i]=nums[i+k],因此最后符合题意的数组一定是有循环节的,循环长度为
k
k
k。因此我们可以把数组分为
k
k
k组,考虑用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示处理到第
i
i
i组且
[
0
,
i
]
[0,i]
[0,i]组所有元素异或和(
n
u
m
s
[
0
]
x
o
r
…
…
n
u
m
s
[
i
]
nums[0]\ xor……nums[i]
nums[0] xor……nums[i])为
j
j
j的情况下所需要更改的最小元素数。
对于第
i
i
i组元素,假设把它们全部变化为
x
x
x,那么有:
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
−
1
]
[
j
x
o
r
x
]
+
s
i
z
e
(
i
)
−
c
o
u
n
t
(
x
)
)
dp[i][j]=min(dp[i-1][j\ xor\ x]+size(i)-count(x))
dp[i][j]=min(dp[i−1][j xor x]+size(i)−count(x))
其中
s
i
z
e
(
i
)
size(i)
size(i)为第
i
i
i组元素的个数,
c
o
u
n
t
(
x
)
count(x)
count(x)为元素
x
x
x在该组内出现的次数。考虑到
n
u
m
s
[
i
]
<
2
10
nums[i]<2^{10}
nums[i]<210,因此上述算法的复杂度为
O
(
k
∗
2
10
∗
2
10
)
O(k*2^{10}*2^{10})
O(k∗210∗210),会超时。
接下来考虑优化,首先
s
i
z
e
(
i
)
size(i)
size(i)与
x
x
x无关,可以提出去;然后考虑当
c
o
u
n
t
(
x
)
=
0
count(x)=0
count(x)=0时,上述公式可以转换为:
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
−
1
]
[
j
x
o
r
x
]
)
+
s
i
z
e
(
i
)
(
3
)
dp[i][j]=min(dp[i-1][j\ xor\ x])+size(i)\ \ \ (3)
dp[i][j]=min(dp[i−1][j xor x])+size(i) (3)
对于确定的
i
、
j
i、j
i、j,这个可以在
O
(
2
10
)
O(2^{10})
O(210)内计算出来。
当
c
o
u
n
t
(
x
)
≠
0
count(x)\not= 0
count(x)=0时,我们依然需要枚举
x
x
x,但是可以只枚举出现过的
x
x
x,这时公式依然为:
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
−
1
]
[
j
x
o
r
x
]
−
c
o
u
n
t
(
x
)
)
+
s
i
z
e
(
i
)
(
4
)
dp[i][j]=min(dp[i-1][j\ xor\ x]-count(x))+size(i)\ \ \ (4)
dp[i][j]=min(dp[i−1][j xor x]−count(x))+size(i) (4)
但是我们可以发现当
c
o
u
n
t
(
x
)
≠
0
count(x)\not= 0
count(x)=0时,
(
4
)
(4)
(4)一定要小于
(
3
)
(3)
(3),由于我们取的是最小值,因此把
(
4
)
(4)
(4)一起考虑也没有关系。所以不管
x
x
x是否在该组出现过,我们都可以先令
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
−
1
]
[
j
x
o
r
x
]
)
+
s
i
z
e
(
i
)
dp[i][j]=min(dp[i-1][j\ xor\ x])+size(i)
dp[i][j]=min(dp[i−1][j xor x])+size(i),然后再枚举出现过的
x
x
x用
(
4
)
(4)
(4)更新。这样复杂度可以降低到
O
(
(
k
+
n
)
∗
2
10
)
O((k+n)*2^{10})
O((k+n)∗210)。
class Solution {
public:
int minChanges(vector<int>& nums, int k) {
const int maxn=1<<10;
const int inf=0x3f3f3f3f;
int n=nums.size();
vector<int> dp(maxn,inf);
dp[0]=0;
for(int i=0;i<k;i++)
{
int size=0;
unordered_map<int,int> cnt;
for(int j=i;j<n;j+=k)
{
++cnt[nums[j]];
++size;
}
int min_value=*min_element(dp.begin(),dp.end());
vector<int> dp2(maxn,min_value);
for(int j=0;j<maxn;j++)
{
for(const auto &pr:cnt)
dp2[j]=min(dp2[j],dp[j^pr.first]-pr.second);
dp2[j]+=size;
}
dp=move(dp2);
}
return dp[0];
}
};