题目链接
Leetcode.1819 序列中不同最大公约数的数目 Rating : 2540
题目描述
给你一个由正整数组成的数组 n u m s nums nums 。
数字序列的 最大公约数 定义为序列中所有整数的共有约数中的最大整数。
- 例如,序列 [ 4 , 6 , 16 ] [4,6,16] [4,6,16] 的最大公约数是 2 。
数组的一个 子序列 本质是一个序列,可以通过删除数组中的某些元素(或者不删除)得到。
- 例如, [ 2 , 5 , 10 ] [2,5,10] [2,5,10] 是 [ 1 , 2 , 1 , 2 , 4 , 1 , 5 , 10 ] [1,2,1,2,4,1,5,10] [1,2,1,2,4,1,5,10] 的一个子序列。
计算并返回 n u m s nums nums 的所有 非空 子序列中 不同 最大公约数的 数目 。
示例 1:
输入:nums = [6,10,3]
输出:5
解释:上图显示了所有的非空子序列与各自的最大公约数。
不同的最大公约数为 6 、10 、3 、2 和 1 。
示例 2:
输入:nums = [5,15,40,5,6]
输出:7
提示:
- 1 < = n u m s . l e n g t h < = 1 0 5 1 <= nums.length <= 10^5 1<=nums.length<=105
- 1 < = n u m s [ i ] < = 2 ∗ 1 0 5 1 <= nums[i] <= 2 * 10^5 1<=nums[i]<=2∗105
解法:数论 & 计数
一个长度为 n n n 的数组的子序列个数为 2 n − 1 2^n - 1 2n−1,本题 n = 1 0 5 n = 10^5 n=105,所以直接回溯枚举的话肯定会超时。
所以我们可以考虑从 值域 入手。因为 n u m s [ i ] nums[i] nums[i] 最大是 2 × 1 0 5 2\times10^5 2×105,所以 n u m s nums nums 任意一个子序列的最大公约数也肯定是 ≤ 2 × 1 0 5 \leq 2\times10^5 ≤2×105的。
那我们就可以考虑枚举 最大公约数 g g g 的倍数:
- 1 , 2 , 3 , 4 , 5 , 6... 1,2,3,4,5,6... 1,2,3,4,5,6...
- 2 , 4 , 6 , 8 , 10 , 12... 2,4,6,8,10,12... 2,4,6,8,10,12...
- 3 , 6 , 9 , 12 , 15 , 18 , . . . 3,6,9,12,15,18,... 3,6,9,12,15,18,...
- 4 , 8 , 12 , 16 , 20 , 24 , . . . 4,8,12,16,20,24,... 4,8,12,16,20,24,...
- …
我们设值域为 U U U。对于 1 1 1 的倍数需要枚举 ⌊ U 1 ⌋ \lfloor \frac{U}{1} \rfloor ⌊1U⌋次;对于 2 2 2 的倍数需要枚举 ⌊ U 2 ⌋ \lfloor \frac{U}{2} \rfloor ⌊2U⌋次;对于 3 3 3 的倍数需要枚举 ⌊ U 3 ⌋ \lfloor \frac{U}{3} \rfloor ⌊3U⌋次;对于 4 4 4 的倍数需要枚举 ⌊ U 4 ⌋ \lfloor \frac{U}{4} \rfloor ⌊4U⌋次。。。
⌊ U 1 ⌋ + ⌊ U 2 ⌋ + ⌊ U 3 ⌋ + ⌊ U 4 ⌋ . . . + ⌊ U U ⌋ ≤ U × ( 1 1 + 1 2 + 1 3 + 1 4 + . . . + 1 U ) \lfloor \frac{U}{1} \rfloor + \lfloor \frac{U}{2} \rfloor +\lfloor \frac{U}{3} \rfloor +\lfloor \frac{U}{4} \rfloor ...+\lfloor \frac{U}{U} \rfloor \leq U \times (\frac{1}{1} + \frac{1}{2} +\frac{1}{3} + \frac{1}{4}+...+\frac{1}{U} ) ⌊1U⌋+⌊2U⌋+⌊3U⌋+⌊4U⌋...+⌊UU⌋≤U×(11+21+31+41+...+U1)
( 1 1 + 1 2 + 1 3 + 1 4 + . . . + 1 U ) (\frac{1}{1} + \frac{1}{2} +\frac{1}{3} + \frac{1}{4}+...+\frac{1}{U} ) (11+21+31+41+...+U1) 是 调和级数,这部分是近似于 O ( l o g U ) O(logU) O(logU) 的。
所以对于每一个数 i i i 的倍数,我们判断它是否在 n u m s nums nums 中(通过哈希表快速判断)。如果在数组中,就将它与 g g g (初始 g = 0 g = 0 g=0)取最大公约数。等到循环结束,判断 g = i g = i g=i。如果成立,说明 n u m s nums nums 中确实存在一个 最大公约数为 i i i 的子序列,答案 +1。
时间复杂度: O ( U × l o g U ) O(U\times logU) O(U×logU)
C++代码:
class Solution {
public:
int countDifferentSubsequenceGCDs(vector<int>& nums) {
int mx = *max_element(nums.begin(),nums.end());
vector<bool> st(mx + 1);
for(auto x:nums) st[x] = true;
int ans = 0;
for(int i = 1;i <= mx;i++){
int g = 0;
for(int j = i;j <= mx && g != i;j += i){
if(st[j]) g = gcd(g , j);
}
if(g == i) ans++;
}
return ans;
}
};