lc1819——枚举因数并判定,两种解法

https://leetcode-cn.com/problems/number-of-different-subsequences-gcds/solution/mei-ju-yin-shu-bing-pan-ding-liang-chong-fj6e/

传送门

法一

力扣上大多数题解都是法一。因为值域是2e5,又是和公约数有关,所以肯定会考虑枚举因数。这里就考虑枚举i,并判定是否存在一个子序列的gcd是i

开桶c[]记录每个数出现次数,枚举i的所有倍数j*i,则sum(c[j*i])就找到了数组里i的倍数构成的集合。对于每个合法j,求它们的gcd。若求得gcd为1,则说明该集合的gcd就是i,则i对答案有1的贡献;否则i对答案无贡献。

时间复杂度O(n*logn*logn)

法二

这位神犇的解法是法二。定义c[i]为数组中是i的倍数的数字个数。对于i的倍数j*i,j >= 2,因为j*i的倍数的集合一定是i的倍数的集合的子集,所以总是满足c[j*i] <= c[i]。对于所有c[j*i] == c[i]j,我们应只保留最大的那个j。比如[6,12,24,36]这个集合,c[6*1] == c[3*1] == c[2*1] == c[1] == 4,则只保留j = 6即可。

实现以上想法时,则枚举所有倍数,统计那些c[i]严格大于所有c[j*i]i

时间复杂度O(n*logn)。但是实测跑得比法一慢。

代码
法一
"use strict";

var countDifferentSubsequenceGCDs = function(a) {
  let gcd = (x, y) => !y ? x : gcd(y, x % y)
  let mx = Math.max(...a)
  let c = new Array(mx + 1).fill(0)
  for (let v of a) c[v]++
  let ans = 0
  for (let i = 1; i <= mx; ++i) {
    let g = 0
    for (let j = 1; j * i <= mx; ++j) {
      if (!c[j * i]) continue
      g = gcd(g, j)
      if (g === 1) break
    }
    ans += g === 1
  }
  return ans
};
法二
"use strict";

var countDifferentSubsequenceGCDs = function(a) {
  let gcd = (x, y) => !y ? x : gcd(y, x % y)
  let mx = Math.max(...a)
  let vis = Array(mx + 1).fill(0)
  let c = Array(mx + 1).fill(0)
  for (let v of a) vis[v]++
  for (let i = 1; i <= mx; ++i) {
    let tot = 0
    for (let j = 1; j * i <= mx; ++j) tot += vis[j * i]
    c[i] = tot
  }
  let ans = 0
  for (let i = 1; i <= mx; ++i) {
    let fl = c[i] > 0
    if (!fl) continue
    for (let j = 2; j * i <= mx; ++j) {
      if (c[i] <= c[j * i]) {
        fl = false
        break
      }
    }
    ans += fl
  }
  return ans
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值