Medium
题目描述
给你一个正整数数组 nums 和一个正整数 k。
你可以对 nums 执行一次操作,在每次操作中,你可以从 nums 中移除任意一个非重叠的前缀和后缀,使得 nums 保持非空。
你需要找到 nums 的 x 值,即执行此操作使得剩余元素的乘积除以 k 的余数为 x 的方法数。
返回一个大小为 k 的数组 result,其中 result[x] 是 nums 在 0 <= x <= k - 1 范围内的 x 值。
数组的前缀是从数组开头开始并延伸到数组内任意点的子数组。
数组的后缀是从数组内任意点开始并延伸到数组末尾的子数组。
注意,操作中选择的前缀和后缀可以为空。
示例 1:
输入:nums = [1,2,3,4,5], k = 3
输出:[9,2,4]
示例 2:
输入:nums = [1,2,4,8,16,32], k = 4
输出:[18,1,2,0]
示例 3:
输入:nums = [1,1,2,1,1], k = 2
输出:[9,6]
约束条件:
1 <= nums[i] <= 10^91 <= nums.length <= 10^51 <= k <= 5
解题思路
这道题要求我们计算通过移除前缀和后缀后,剩余元素乘积对k取模的结果为每个可能值的方案数。
核心思路:
转化问题:移除前缀和后缀等价于选择一个子数组。我们需要统计所有可能的连续子数组,计算它们的乘积对k取模的结果。
动态规划方法:
- 定义
dp[i][r]表示以位置i结尾、乘积模k等于r的子数组个数 - 对于每个位置i,我们可以:
- 单独成为一个子数组:
dp[i][nums[i] % k] += 1 - 扩展前面的子数组:
dp[i][(prev_r * nums[i]) % k] += dp[i-1][prev_r]
- 单独成为一个子数组:
- 定义
优化空间:由于k最大为5,我们可以用滚动数组优化空间复杂度。
统计答案:遍历所有位置,累加每个余数对应的方案数。
时间复杂度:O(n×k),其中n是数组长度 空间复杂度:O(k)
代码实现
class Solution {
public:
vector<long long> resultArray(vector<int>& nums, int k) {
int n = nums.size();
vector<long long> result(k, 0);
for (int i = 0; i < n; i++) {
vector<long long> dp(k, 0);
dp[nums[i] % k] = 1;
result[nums[i] % k]++;
for (int j = i - 1; j >= 0; j--) {
vector<long long> new_dp(k, 0);
for (int r = 0; r < k; r++) {
if (dp[r] > 0) {
int new_r = (1LL * r * nums[j]) % k;
new_dp[new_r] += dp[r];
}
}
new_dp[nums[j] % k]++;
dp = new_dp;
for (int r = 0; r < k; r++) {
result[r] += dp[r];
}
}
}
return result;
}
};
class Solution:
def resultArray(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
result = [0] * k
for i in range(n):
dp = [0] * k
dp[nums[i] % k] = 1
result[nums[i] % k] += 1
for j in range(i - 1, -1, -1):
new_dp = [0] * k
for r in range(k):
if dp[r] > 0:
new_r = (r * nums[j]) % k
new_dp[new_r] += dp[r]
new_dp[nums[j] % k] += 1
dp = new_dp
for r in range(k):
result[r] += dp[r]
return result
public class Solution {
public long[] ResultArray(int[] nums, int k) {
int n = nums.Length;
long[] result = new long[k];
for (int i = 0; i < n; i++) {
long[] dp = new long[k];
dp[nums[i] % k] = 1;
result[nums[i] % k]++;
for (int j = i - 1; j >= 0; j--) {
long[] newDp = new long[k];
for (int r = 0; r < k; r++) {
if (dp[r] > 0) {
int newR = (int)((long)r * nums[j] % k);
newDp[newR] += dp[r];
}
}
newDp[nums[j] % k]++;
dp = newDp;
for (int r = 0; r < k; r++) {
result[r] += dp[r];
}
}
}
return result;
}
}
var resultArray = function(nums, k) {
const n = nums.length;
const result = new Array(k).fill(0);
for (let i = 0; i < n; i++) {
let dp = new Array(k).fill(0);
dp[nums[i] % k] = 1;
result[nums[i] % k]++;
for (let j = i - 1; j >= 0; j--) {
const newDp = new Array(k).fill(0);
for (let r = 0; r < k; r++) {
if (dp[r] > 0) {
const newR = (r * nums[j]) % k;
newDp[newR] += dp[r];
}
}
newDp[nums[j] % k]++;
dp = newDp;
for (let r = 0; r < k; r++) {
result[r] += dp[r];
}
}
}
return result;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²×k) | 双重循环遍历所有子数组,每次需要更新k个状态 |
| 空间复杂度 | O(k) | 使用长度为k的dp数组存储状态 |