Hard
题目描述
给你一个由正整数组成的数组 nums。
数字序列的 GCD 定义为能够被序列中所有数字整除的最大正整数。
- 例如,序列
[4,6,16]的 GCD 为2。
数组的 子序列 是通过删除数组中的某些元素(可能不删除)而形成的序列。
- 例如,
[2,5,10]是[1,2,1,2,4,1,5,10]的一个子序列。
返回数组 nums 所有 非空 子序列的不同 GCD 的数量。
示例 1:
输入:nums = [6,10,3]
输出:5
解释:上图显示了所有的非空子序列与它们的 GCD。
不同的 GCD 为 6、10、3、2 和 1。
示例 2:
输入:nums = [5,15,40,5,6]
输出:7
提示:
1 <= nums.length <= 10^51 <= nums[i] <= 2 * 10^5
解题思路
解题思路
这道题需要计算所有非空子序列的不同GCD数量。直接枚举所有子序列会超时,需要转换思路。
核心观察:
- 对于一个给定的数值
g,如果存在某个子序列的GCD为g,那么这个子序列中的所有元素都必须是g的倍数 - 如果数组中存在
g的倍数,我们可以检查所有g的倍数组成的子序列,看它们的GCD是否恰好为g
算法步骤:
- 首先统计数组中每个数字的出现情况,并记录最大值
- 对于每个可能的GCD值
g(从1到最大值),检查是否存在GCD为g的子序列:- 找出数组中所有
g的倍数 - 计算这些倍数的GCD
- 如果GCD恰好等于
g,则g是一个有效的子序列GCD
- 找出数组中所有
优化要点:
- 使用哈希表记录数字的存在性,避免重复计算
- 只枚举到数组中的最大值即可
- 利用GCD的性质进行剪枝
这种方法的时间复杂度为 O(max_val * log(max_val)),其中 max_val 是数组中的最大值。
代码实现
class Solution {
public:
int countDifferentSubsequenceGCDs(vector<int>& nums) {
unordered_set<int> numSet(nums.begin(), nums.end());
int maxVal = *max_element(nums.begin(), nums.end());
int result = 0;
for (int g = 1; g <= maxVal; g++) {
int currentGCD = 0;
for (int multiple = g; multiple <= maxVal; multiple += g) {
if (numSet.count(multiple)) {
if (currentGCD == 0) {
currentGCD = multiple;
} else {
currentGCD = __gcd(currentGCD, multiple);
}
if (currentGCD == g) {
result++;
break;
}
}
}
}
return result;
}
};
class Solution:
def countDifferentSubsequenceGCDs(self, nums: List[int]) -> int:
from math import gcd
num_set = set(nums)
max_val = max(nums)
result = 0
for g in range(1, max_val + 1):
current_gcd = 0
for multiple in range(g, max_val + 1, g):
if multiple in num_set:
if current_gcd == 0:
current_gcd = multiple
else:
current_gcd = gcd(current_gcd, multiple)
if current_gcd == g:
result += 1
break
return result
public class Solution {
public int CountDifferentSubsequenceGCDs(int[] nums) {
HashSet<int> numSet = new HashSet<int>(nums);
int maxVal = nums.Max();
int result = 0;
for (int g = 1; g <= maxVal; g++) {
int currentGCD = 0;
for (int multiple = g; multiple <= maxVal; multiple += g) {
if (numSet.Contains(multiple)) {
if (currentGCD == 0) {
currentGCD = multiple;
} else {
currentGCD = GCD(currentGCD, multiple);
}
if (currentGCD == g) {
result++;
break;
}
}
}
}
return result;
}
private int GCD(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
}
var countDifferentSubsequenceGCDs = function(nums) {
const gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
const max = Math.max(...nums);
const exists = new Array(max + 1).fill(false);
for (const num of nums) {
exists[num] = true;
}
let count = 0;
for (let i = 1; i <= max; i++) {
let currentGCD = 0;
for (let j = i; j <= max; j += i) {
if (exists[j]) {
if (currentGCD === 0) {
currentGCD = j;
} else {
currentGCD = gcd(currentGCD, j);
}
if (currentGCD === i) {
count++;
break;
}
}
}
}
return count;
};
复杂度分析
| 复杂度类型 | 复杂度 |
|---|---|
| 时间复杂度 | O(max_val × log(max_val)) |
| 空间复杂度 | O(n) |
说明:
- 时间复杂度:外层循环遍历1到max_val,内层循环枚举倍数,GCD计算的复杂度为O(log(max_val))
- 空间复杂度:主要用于存储数字的哈希表,大小为O(n)