Day2 LeetCode-1734-解码异或后的排列-中等


1734. 解码异或后的排列

一、题目描述

给你一个整数数组 perm ,它是前 n 个正整数的排列,且 n 是个 奇数 。

它被加密成另一个长度为 n - 1 的整数数组 encoded ,满足 encoded[i] = perm[i] XOR perm[i + 1] 。比方说,如果 perm = [1,3,2] ,那么 encoded = [2,1] 。

给你 encoded 数组,请你返回原始数组 perm 。题目保证答案存在且唯一。

提示:

  • 3 <= n < 105
  • n 是奇数。
  • encoded.length == n - 1

二、基础题解

设置了30分钟的倒计时,还是没能做出来。。。
第一时间想到的题解就是暴力深度优先,题解如下:

1、遍历数字1~n,对于每个数字 i,调用一次DFS子函数
  1.1 DFS(encoded,0,visits,i,n)
  1.2 如果数组perm的大小 == n,成功,直接返回ans;否则继续遍历
//
DFS子函数:(vector& encoded,int ei,vector& visits,int ai,int n)
以数字 i 作为perm数组的第一个元素,不断递归:
  >数字 i 和 encoded数组的当前元素encoded[ei]进行异或,令结果为x,如果visits[x] = 0 并且 x <= n,则:
    >visits [ i ] = 1
    >perm.push(i)
    >i = x
    >DFS(encoded,ei+1,visits,x,n)
    >如果perm 大小 < n :perm.pop()
    >visits [ i ] = 0
  >如果ei = n-1,说明encoded已经遍历到最后了,说明成功找到perm,执行perm.push(ai)之后,就可以直接返回了

参考代码

vector<int> perm; //答案数组
/*
 * ei 表示encoded数组的当前位置,ai表示答案数组中的当前元素,n为答案数组的理论长度
 */
void DFS(vector<int>& encoded,int ei,vector<int>& visits,int ai,int n){
    
    
    if(ei < n-1 && (ai ^ encoded[ei]) <= n && visits[ai] == 0){
    
    
        visits[ai] = 1;
        perm.push_back(ai);
        DFS(encoded,ei+1,visits,ai ^ encoded[ei],n);
        if(perm.size() < n){
    
    
            perm.pop_back();
        }
        visits[ai] = 0;
    }else if( ei == n-1){
    
    
        perm.push_back(ai);
    }
}
vector<int> decode0(vector<int>& encoded) {
    
    
    int n = encoded.size()+1;
    vector<int> visits(n+1,0);
    visits[0] = 1;
    for (int i = 1; i <= n ; ++i) {
    
    
        DFS(encoded,0,visits,i,n);
        if(perm.size() == n){
    
    
            return perm;
        }
    }
    return perm;
}

性能分析

时间复杂度:O(n^2)
说明:遍历数字1到n,就是O(n)了,而对于遍历的每一个数字,都会调用DFS,进行深度优先寻找,深度可能是在1到n之间,所以时间复杂度的上界就是O(n^2)。

空间复杂度:O(n)
说明:额外的标记数组visits大小是n,递归深度调用栈的深度最大也是n。

结果
在这里插入图片描述
额…还是败在了10^5上

三、进阶题解

上述题解简单来说就是在不知道perm数组第一个元素的前提下,盲目地都尝试了一遍。但其实,第一个元素是可以在O(n)时间下找出来的!题解如下:

预备知识:^ 是 异或符号
a ^ b = c
a ^ c = b
b ^ c = a

题解
1、找perm的第一个元素
  1.1 获取perm所有元素的异或结果,记为allNumXor
    perm的所有元素就是1 ~ n,遍历1 ~ n就行了
  1.2 获取perm除第一个元素外所有元素的异或结果,记为withoutNum1Xor
    将encoded数组中所有奇数位元素都异或,得到的就是withoutNum1Xor,这个不难,展开一下就行了
  1.3 withoutNum1Xor与allNumXor异或,得到的就是perm的第一个元素了
2、根据第一个元素,不断和encoded的元素进行异或,并将异或结果放入perm数组中
3、最终构建的perm就是答案perm了

性能分析

时间复杂度:O(n)
说明:遍历了三次数组,最大的数组不超过n。

空间复杂度:O(1)
说明:结果数组perm除外的话,只用了两个变量withoutNum1Xor和allNumXor,所以是常数空间。

四、总结

数组perm是前n个正整数的全排列,这个条件没有很好地利用起来,只是用作标记数组了;并且异或的一些性质也没很好地掌握,所以没能得到好的题解。

五、代码

#include <bits/stdc++.h>
using namespace std;
vector<int> decode(vector<int>& encoded) {
    
    
    int n = encoded.size()+1;
    int allNumXor = 0;
    for (int i = 1; i <= n; ++i) {
    
    
        allNumXor ^= i;
    }
    int withoutNum1Xor = 0;
    int index = 1;
    while(index < n-1){
    
    
        withoutNum1Xor ^= encoded[index];
        index +=2;
    }
    int num1 = allNumXor ^ withoutNum1Xor;
    vector<int> perm;
    perm.push_back(num1);
    index = 0;
    while(index < n-1){
    
    
        perm.push_back(num1 ^ encoded[index]);
        num1 = num1 ^ encoded[index++];
    }
    return perm;
}

int main() {
    
    
    vector<int> v = {
    
    3,1};
    decode(v);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40466537/article/details/116693566