2017滴滴出行笔试题:异或和为0的最大区间个数
两个bit的异或(下文均用^代表异或运算):1^0=1 0^1=1 1^1=0 0^0=0,也就是左右元素不同时为1,相同时为0。
对于两个int的异或,就是对它的二进制表示的每一位进行异或运算,比如2^5=binary(010^101)=binary(111)=7
并且异或运算满足交换律、结合律,即a^b=b^a,a^b^c=(a^b)^c=a^(b^c)
异或运算还有性质:x^0=x,x^x=0(这里可以得出推论:若x^y=0,则x=y;)
OK,到此就可以分析题目了,题目给出数组a[N]找出所有不重叠区间,区间的数字xor和为0。
这里定义f(i,j)=a[i]^a[i+1]^...^a[j],这里值得注意的是不重叠区间,那么假如有一对重叠区间的xor和为0,那我们应该选择哪一组呢?
设重叠区间为[b,c],两个区间为[a,c]和[b,d],其中a<=b<c<=d。
显然应该选择[a,c],因为这样相当于在[c,N]区间寻找xor和为0的子区间数量,而c<=d<N,[c,N]是在[d,N]的基础上多了0个以上数,那么子区间数量肯定不少于[d,N]。
因此思路就是先找出最左边的子区间,然后在右边剩余区间重复以上操作。
最左边也就是区间[a,b]满足b最小,也就是b从0到n,如果找到了一组异或和为0的区间[a0,b0],那么b就从b0+1开始,但是a不用从0开始,而是也从a0+1开始。
因为若存在f(a0,b0+t)=0,那么易证f(b0+1,b0+t)也为0,可以分成2个异或和为0的区间。
更详细的证明就不用说了,有这样的思路后直接上代码吧。
#include <iostream> #include <vector> #include <algorithm> using namespace std; void solution(int a[], int n) { int cnt = 0; // 闭区间个数 vector<int> v; v.reserve(n); int low = 0; // 闭区间左边界的下限(即上一个找到异或和为0的闭区间右边界+1) for (int i = 0; i < n; i++) { // 查找以i为闭区间的右边界的区间是否存在满足异或和为0的 v.clear(); // 依次加入a[k]、a[k-1]^a[k]、...、a[low]^a[low+1]^...^a[k] v.push_back(a[i]); for (int j = i - 1; j >= low; j--) { v.push_back(v.back() ^ a[j]); } if (find(v.begin(), v.end(), 0) != v.end()) { // 存在异或和为0的子区间 cnt++; low = i + 1; // 更新闭区间左边界, 避免重复查找 } } cout << cnt << endl; } int main() { int n; cin >> n; vector<int> v(n); for (int i = 0; i < n; i++) { cin >> v[i]; } solution(&v[0], n); return 0; }