Easy
题目描述
你有一个炸弹需要拆除,时间不多了!你的线人会提供一个长度为 n 的环形数组 code 和一个密钥 k。
要破译密码,你必须替换每个数字。所有数字都是同时替换的。
- 如果 k > 0,将第 i 个数字替换为接下来 k 个数字的和。
- 如果 k < 0,将第 i 个数字替换为前面 -k 个数字的和。
- 如果 k == 0,将第 i 个数字替换为 0。
由于 code 是环形的,code[n-1] 的下一个元素是 code[0],code[0] 的前一个元素是 code[n-1]。
给定环形数组 code 和整数密钥 k,返回解密后的代码来拆除炸弹!
示例 1:
输入:code = [5,7,1,4], k = 3
输出:[12,10,16,13]
解释:每个数字都被接下来 3 个数字的和替换。解密后的代码是 [7+1+4, 1+4+5, 4+5+7, 5+7+1]。注意数字是环形的。
示例 2:
输入:code = [1,2,3,4], k = 0
输出:[0,0,0,0]
解释:当 k 为零时,数字被替换为 0。
示例 3:
输入:code = [2,4,9,3], k = -2
输出:[12,5,6,13]
解释:解密后的代码是 [3+9, 2+3, 4+2, 9+4]。注意数字再次环形。如果 k 是负数,则是前面数字的和。
提示:
- n == code.length
- 1 <= n <= 100
- 1 <= code[i] <= 100
- -(n - 1) <= k <= n - 1
解题思路
这道题要求我们对环形数组进行特殊的求和操作。核心在于处理环形数组的索引计算和三种不同的情况。
思路分析:
k = 0 的情况:最简单,所有位置都填 0。
k > 0 的情况:对于每个位置 i,需要计算后面 k 个数字的和。由于是环形数组,需要使用模运算处理索引越界。
k < 0 的情况:对于每个位置 i,需要计算前面 |k| 个数字的和。同样需要处理环形索引。
优化方案:
- 方法一:暴力解法,对每个位置直接计算所需的和,时间复杂度 O(n×|k|)
- 方法二:滑动窗口优化,先计算第一个窗口的和,然后滑动窗口更新,时间复杂度 O(n)
由于题目约束较小(n ≤ 100),两种方法都可行,但滑动窗口更优雅。这里给出滑动窗口的解法。
实现要点:
- 使用模运算
(i + n) % n处理负索引 - 预先计算窗口和,然后滑动更新
- 分别处理 k > 0 和 k < 0 的情况
代码实现
class Solution {
public:
vector<int> decrypt(vector<int>& code, int k) {
int n = code.size();
vector<int> result(n, 0);
if (k == 0) return result;
// 计算初始窗口和
int windowSum = 0;
int start, end;
if (k > 0) {
// k > 0: 计算后面k个数字的和
start = 1;
end = k;
} else {
// k < 0: 计算前面|k|个数字的和
start = n + k;
end = n - 1;
}
// 计算初始窗口和
for (int i = start; i <= end; i++) {
windowSum += code[i % n];
}
// 滑动窗口
for (int i = 0; i < n; i++) {
result[i] = windowSum;
// 移除窗口最左边的元素,添加下一个元素
windowSum -= code[start % n];
start++;
end++;
windowSum += code[end % n];
}
return result;
}
};
class Solution:
def decrypt(self, code: List[int], k: int) -> List[int]:
n = len(code)
result = [0] * n
if k == 0:
return result
# 计算初始窗口和
window_sum = 0
if k > 0:
# k > 0: 计算后面k个数字的和
start, end = 1, k
else:
# k < 0: 计算前面|k|个数字的和
start, end = n + k, n - 1
# 计算初始窗口和
for i in range(start, end + 1):
window_sum += code[i % n]
# 滑动窗口
for i in range(n):
result[i] = window_sum
# 移除窗口最左边的元素,添加下一个元素
window_sum -= code[start % n]
start += 1
end += 1
window_sum += code[end % n]
return result
public class Solution {
public int[] Decrypt(int[] code, int k) {
int n = code.Length;
int[] result = new int[n];
if (k == 0) return result;
// 计算初始窗口和
int windowSum = 0;
int start, end;
if (k > 0) {
// k > 0: 计算后面k个数字的和
start = 1;
end = k;
} else {
// k < 0: 计算前面|k|个数字的和
start = n + k;
end = n - 1;
}
// 计算初始窗口和
for (int i = start; i <= end; i++) {
windowSum += code[i % n];
}
// 滑动窗口
for (int i = 0; i < n; i++) {
result[i] = windowSum;
// 移除窗口最左边的元素,添加下一个元素
windowSum -= code[start % n];
start++;
end++;
windowSum += code[end % n];
}
return result;
}
}
var decrypt = function(code, k) {
const n = code.length;
const result = new Array(n);
if (k === 0) {
return new Array(n).fill(0);
}
for (let i = 0; i < n; i++) {
let sum = 0;
if (k > 0) {
for (let j = 1; j <= k; j++) {
sum += code[(i + j) % n];
}
} else {
for (let j = 1; j <= Math.abs(k); j++) {
sum += code[(i - j + n) % n];
}
}
result[i] = sum;
}
return result;
};
复杂度分析
| 复杂度 | 大小 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
说明:
- 时间复杂度:O(n),只需要遍历一次数组,每次窗口滑动都是 O(1) 操作
- 空间复杂度:O(1),除了结果数组外,只使用了常数级别的额外空间