1486. 数组异或操作

思路:
就我的小脑袋瓜,除了for循环遍历,还能想到什么高级的数学方法呀?
我下意识想到用异或运算的性质,但是没想到用的哪个。看了题解确信了数学方法我做不出来 。

代码:

class Solution {
public:
    int xorOperation(int n, int start) {
        int res=0;
        for(int i=0;i<n;++i){
            res ^= start+2*i;
        }
        return res;
    }
};

看这题解看不懂,睡一觉,迷迷糊糊想明白了,就突然清醒爬起来记录了。
首先我们考虑把start+2 * i中的2提取出来,那么就是 start / 2 + i => s+i,那么start丢失了原来的最后一位。
当我们进行异或 原式子(start+2 * 0) ^ (start+2 * 1).... 右移1位后(s+0) ^ (s+1)... * 2,即使我们乘以2 ,但其实这样的变换是不相等的,因为我们将start右移一位后,在左移回来我们是丢失了最后一位的数的,左移回来已经全为0.所以,我们还要加上原式进行异或后的最后一位,就是start+2 * i 各项异或后的最后一位。

那么问题来了,如何求最后一位呢?这里就需要找规律了。当start为偶数时,2 ^ 4 ^ 6...,不论有偶数个还是奇数个相异或,我们其实可以得到一个结论,最后一位是0。当start为奇数是,此时如果n为奇数,及 某个奇数+2 * i 有奇数项个向异或,那么我们得到结论,在最后一位为1,举个栗子 1 ^ 3 ^ 5,其二进制最后一位为1. 同理当n为偶数时,异或结果二进制最后一位为0,因为奇数个为1,那么再来一个奇数自然相异或就为0了(一个奇数的二进制最后一位一定为1)。
OK,我们得到规律后,如何转化为逻辑表达式呢? n&start,如果两个都为奇数,那么必然最后一位为1,否则任何一个不为奇数最后一位都为0,最后在和1相与,就能提取最后一位了。那么 lastbit=n&start&1.

接下来还是一个规律,可以简化不用一个个异或。并未后面最重要的算法做准备。
我们把0-9的二进制数列出来,然后在列出0-0,0-1,0-2...0-9的异或值,0-9的异或值是什么意思?就是0 ^ 1 ^ 2 ^ 3 ^ 4... ^ 9得到的结果。

n 二进制 异或值(0-n)
0  0000  0000
1  0001  0001
2  0010  0011
3  0011  0000
4  0100  0100
5  0101  0001
6  0110  0111
7  0111  0000
8  1000  1000
9  1001  0001

通过对比我们可以发现 n % 4 == 0的异或值都为n即其本身,如4,8;n % 4 == 1的数异或值都为1,如1,9;n % 4 == 2的异或值都为n+1,如2,6.最后n % 4 == 3的异或值都为0.

上述的结论会服务我们最后的算法。
根据异或的性质,我们有3 ^ 4 ^ 5 ^ 6 ^ 7 ^ 8 ^ 9=(1 ^ 2) ^ (1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 6 ^ 7 ^ 8 ^ 9).因为自己和自己异或就为0了嘛,0和任意数异或都为任意数本身,所以就把1^2这个重复的给弄为0了,还不影响后面的3-9的异或。
好的,那我们的求解的 s+i 异或与此相同,(s+0) ^ (S+1)... ^ (s+n-1) = calxor(s-1) ^ calxor(s+n-1).
有朋友要问了:哎,为什么这里是s+n-1呢?答:因为题目说n的下标从0开始,故从0开始数到n-1才是n。
calxor就是我们上面统计出来的n%4的规律,对比我们举得例子,calxor(s-1)=(1 ^ 2),同样calxor(s+n-1)=(1 ^ 2.. ^ 9)。
那么我们就能得到答案了,我们把n个数的异或转换为了两个数的异或,O(n)的时间复杂度降到了O(1)
那么我们算除了s+i各项异或的结果后,再把lastbit加上即可。

代码:

class Solution {
public:
    int calxor(int n){
        switch(n%4){
            case 0: return n;
            case 1: return 1;
            case 2: return n+1;
        }
        return 0;  //n%4==3的情况
    }
    int xorOperation(int n, int start) {
        int lasebit = n&start&1;  //求解最后一个比特
        int s=start>>1;  //转化为s+i
        int res = calxor(s-1)^calxor(s+n-1); //就是我们说的那个算法
        return (res<<1)+lasebit;
    }
};

参考题解

posted @ 2021-05-11 21:26  Mrsdwang  阅读(116)  评论(0编辑  收藏  举报