题目
分析解法
- 由于a = arr[i…j-1]
- b = arr[j…k]
- 如果a = b,则a^b 必等于0,也就是arr[i] ^ a[…k] = 0。
也就是说,若a = b满足,则 i…k 的连续序列的异或值就等于0,那么组合数如何求呢?由于i…k中 i 和 k 肯定是确定了的,得到一个序列异或为0后,我们的i 、j、k就可以通过将j的位置不断改变得到组合数。
比如arr[0]^ arr[1]^ arr[2] = 0,则组合有:(0,1,2)、(0,2,2)因 j 可以等于k,所以按照这个规律可以知道,我们只要得到一个满足的连续数组序列,就可以根据它的长度得到组合数。
其实对于这类题,还有一个通用的思路就是找前缀异或的关系,通过一个数组可以存储相应的前缀和,我们可以找出关系。
两层循环代码:
class Solution {
public:
int countTriplets(vector<int>& arr) {
int ans = 0;
//通过枚举得到i..j的连续异或等于0的所有情况
for(int i=0;i<arr.size();i++){
int tmp = arr[i];
for(int j=i+1;j<arr.size();j++){
//枚举各个连续异或
tmp ^= arr[j];
if(!tmp){
//累加符合条件的组合数
ans += j-i;
}
}
}
return ans;
}
};
一层循环代码
首先我们前面已经明白一个事实那就是假设区间 [i, k] 的异或值为 0,那么 j 可以有 k-i 种可能性。假设我们遍历到 k 时的前缀异或值为 x, 而前缀异或值 x 在 k 的前面出现了 4 次前缀分别出现在 [i1, i2, i3, i4], 那么 j 有多少种可能性?
(
k
−
i
1
)
+
(
k
−
i
2
)
+
(
k
−
i
3
)
+
(
k
−
i
4
)
=
4
∗
k
−
(
i
1
+
i
2
+
i
3
+
i
4
)
(k - i1) + (k - i2) + (k - i3) + (k - i4) = 4*k - (i1+i2+i3+i4)
(k−i1)+(k−i2)+(k−i3)+(k−i4)=4∗k−(i1+i2+i3+i4)
因此我们得到
a
n
s
+
=
c
o
u
n
t
[
v
a
l
]
∗
k
−
s
u
m
I
n
d
e
x
[
v
a
l
]
;
ans += count[val]*k - sumIndex[val];
ans+=count[val]∗k−sumIndex[val];
所以我们只需要通过hash表记录有多少个前缀异或值相同的元素,通过相同值其实异或等于0的规律,根据次数和下标总和(该次出现之前的)便可得到答案。
class Solution {
public:
int countTriplets(vector<int>& arr) {
int n = arr.size(), ans = 0, val = 0;
//记录前缀异或的下标总和以及该值出现的次数
unordered_map<int, int> count, sumIndex;
for(int k = 0; k < n; ++k)
{
val ^= arr[k];
ans += count[val]*k - sumIndex[val];
//由于我们上面计算需要的是该前缀和之前的前缀和值相等的次数。
//而下标也是一个道理,所以hash更新的速度应该要慢 值的判断 速度一拍。
//所以进行计数的时候是对val^arr[k]^arr[k]两次从而还是得到val也就是上一次前缀和
++count[val^arr[k]];
sumIndex[val^arr[k]] += k;
}
return ans;
}
};