一、题目描述
给你一个整数数组 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;
}