[C++&Rust]LeetCode No.1486 数组异或操作(每日一题)

原贴地址:http://blog.leanote.com/post/dawnmagnet/c8deab0f58d7

题目

在这里插入图片描述

思路分析

这个题目就算我们暴力做,也只是O(n)的复杂度,而且题目的数据给的也不是很大,暴力一定非常快。(窃喜
于是我们暴力做了一下,很快啊,就出来了,直接就AC了,还只用了0ms。

impl Solution {
    pub fn xor_operation(n: i32, mut start: i32) -> i32 {
        let mut res = 0;
        for i in 0..n {
            res ^= (start + 2 * i);
        }
        res
    }
}

那么请问我们还有继续往下优化的必要吗?
当然有。
往下优化,复杂度可能是O(1)或者O(lgn),一般来说,我们要有基本的概念。
怎么样才能让复杂度到达O(1)呢?一般来说是答案是有规律的,套个公式什么的直接就能出答案。
怎么样才能让复杂度到达O(lgn)呢?一般来说是通过除2来构造的,除2之前之后获取一些信息,再组合组合。这样才能得到O(lgn)的答案。

给大家一些做题目时候的小tips,遇到这种数列找规律的题目,先打印出来看看,是否符合规律(基本上都能肉眼观察出来)

以下是初始值为0的时候前20项
0 2 6 0 8 2 14 0 16 2 22 0 24 2 30 0 32 2 38 
以下是初始值为1的时候前20项
1 2 7 0 9 2 15 0 17 2 23 0 25 2 31 0 33 2 39 
以下是初始值为2的时候前20项
2 6 0 8 2 14 0 16 2 22 0 24 2 30 0 32 2 38 0 
以下是初始值为3的时候前20项
3 6 1 8 3 14 1 16 3 22 1 24 3 30 1 32 3 38 1 

不难看出,以任何数字为初始值开始都是有规律可循的。而且每一个都是4项为一组。

初始值规律
0* 2 * 0
1* 2 * 0
22 * 0 *
33 * 1 *

不难看出都是有规律可循的,而只要定位了这些规律点,就非常好求每一项了,比如求起始为0的第五项,我们只需要在里面找到第五项的位置,就是2左边的位置,再用2去异或2 * n 也就是2 ^ 10 = 8,按照这个方法,只要我们确定了特征点,在去反推任何位置的数字,只需要O(1)的时间,最多不会超过三步

  • 对4取余,在特征数组中定位
  • 如果是定值,则返回定值
  • 如果不是定值,则拿定值反推

OK,就好了。
而对于数组的预处理,我们只需要定一个长度为4的特征数组,从第一项循环到第八项,就可以确定这个数组里哪些数字是不变的常量,我们就用常量表示,哪些数字是不确定的,我们就将其标注为-1(与表格中的*等价)。
整个算法的复杂度是O(1)级别的,非常快速也非常简单易懂。

Rust代码

impl Solution {
    pub fn xor_operation(mut n: i32, mut start: i32) -> i32 {
        let mut sto = [-1; 4];
        let mut tmp = 0;
        for i in 0..4 {
            tmp ^= (start + 2 * i);
            sto[i as usize] = tmp;
        }
        for i in 0..4 {
            tmp ^= (start + 2 * (i + 4));
            if sto[i as usize] != tmp {
                sto[i as usize] = -1;
            }
        }
        let n = n as usize;
        match sto[(n - 1) % 4] {
            -1 => sto[n % 4] ^ (start + 2 * n as i32),
            _ => sto[(n - 1) % 4]
        }
    }
}

C++代码

class Solution {
public:
    int xorOperation(int n, int start) {
        vector<int> sto = {0, 0, 0, 0};
        int tmp = 0;
        for (int i = 0; i < 4; ++i) {
            tmp ^= (start + 2 * i);
            sto[i] = tmp;
        }
        for (int i = 0; i < 4; ++i) {
            tmp ^= (start + 2 * (i + 4));
            if (sto[i] != tmp)
                sto[i] = -1;
        }
        if (sto[(n - 1) % 4] == -1) 
            return sto[n % 4] ^ (start + 2 * n);
        else 
            return sto[(n - 1) % 4];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值