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;
}

 

posted @ 2017-09-13 00:55  Harley_Quinn  阅读(613)  评论(0编辑  收藏  举报