题目描述如下
给你一个由正整数组成的数组 nums 。
数字序列的 最大公约数 定义为序列中所有整数的共有约数中的最大整数。
例如,序列 [4,6,16] 的最大公约数是 2 。
数组的一个 子序列 本质是一个序列,可以通过删除数组中的某些元素(或者不删除)得到。
例如,[2,5,10] 是 [1,2,1,2,4,1,5,10] 的一个子序列。
计算并返回 nums 的所有 非空 子序列中 不同 最大公约数的 数目 。
采用树形dp思想的写法如下
但是是超时的,从数据规模来看,要达到O(nlogn),而树形里面用到了递归,所以时间第二维里的时间复杂度更高一点
class Solution {
public:
int countDifferentSubsequenceGCDs(vector<int>& nums) {
int sum=0;
int len=nums.size();
vector<vector<set<int>>>dp(len,vector<set<int>>(len));
function<int(int, int)> gcd;
gcd = [&](int a, int b) -> int {
return a % b ? gcd(b, a % b) : b;
};
map<int,int>mp;
for(int i=0;i<nums.size();i++)
for(int j=i;j>=0;j--){
if(i==j){
dp[j][i].insert(nums[i]);
if(!mp[nums[i]]){
mp[nums[i]]++;
sum++;
}
}
else if(i-j==1){
int k=gcd(nums[i],nums[j]);
dp[j][i].insert(k);
dp[j][i].insert(nums[i]);
dp[j][i].insert(nums[j]);
if(!mp[k]){
mp[k]++;
sum++;
}
}
else{
//分别存储三个子序列的最大公约数
dp[j][i].insert(dp[j][i-1].begin(),dp[j][i-1].end());
dp[j][i].insert(dp[j+1][i].begin(),dp[j+1][i].end());
for(auto &t:dp[j][i-1]){
int temp=gcd(t,nums[i]);
dp[j][i].insert(temp);
if(!mp[temp]){
mp[temp]++;
sum++;
}
}
for(auto &t:dp[j+1][i]){
int temp=gcd(t,nums[j]);
dp[j][i].insert(temp);
if(!mp[temp]){
mp[temp]++;
sum++;
}
}
}
}
return sum;
}
};
结合高票的答案写的如下
- 其实也结合了dp的思想,通过r[i]=__gcd(r[i],n)不断更新r[i]这个位i代表的最大公约数(即i最小值是1)
class Solution {
public:
int countDifferentSubsequenceGCDs(vector<int>& nums) {
int maxnum=*max_element(nums.begin(),nums.end());
vector<int> r(maxnum+1); //去重,而且自动排序
for(auto &n:nums)
for (int i = 1; i*i <= n; ++i)
{
if (n%i == 0)
{
if(!r[i])
r[i]=n;
else r[i]=__gcd(r[i],n);
int z=n/i;
if(!r[z])
r[z]=n;
else r[z]=__gcd(r[z],n);
}
}
int ans=0;
for(int i=1;i<=r.size();i++)
if(i==r[i])
ans++;
return ans;
}
};