LeetCode 2176. 统计数组中相等且可以被整除的数对

文章介绍了两种解法来解决寻找数组中满足nums[i]=nums[j]且(i*j)能被k整除的数对(i,j)的问题。第一种是直接双层循环,时间复杂度为O(n^2),第二种利用最大公约数优化,时间复杂度降为O(nlogn)。

给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 k ,请你返回满足 0 <= i < j < n ,nums[i] == nums[j] 且 (i * j) 能被 k 整除的数对 (i, j) 的 数目 。

示例 1:

输入:nums = [3,1,2,2,2,1,3], k = 2
输出:4
解释:
总共有 4 对数符合所有要求:

  • nums[0] == nums[6] 且 0 * 6 == 0 ,能被 2 整除。
  • nums[2] == nums[3] 且 2 * 3 == 6 ,能被 2 整除。
  • nums[2] == nums[4] 且 2 * 4 == 8 ,能被 2 整除。
  • nums[3] == nums[4] 且 3 * 4 == 12 ,能被 2 整除。

1 <= nums.length <= 100
1 <= nums[i], k <= 100

解法一:直接模拟题意:

class Solution {
public:
    int countPairs(vector<int>& nums, int k) {
        int ans = 0;
        for (int i = 0; i < nums.size(); ++i) {
            for (int j = i + 1; j < nums.size(); ++j) {
                if ((nums[i] == nums[j]) && ((i * j) % k == 0)) {
                    ++ans;
                }
            }
        }

        return ans;
    }
};

如果输出数组nums的长度为n,此算法时间复杂度O(n2^22),空间复杂度O(1)。

解法二:i * j == q * k,其中q为整数,两边同时除gcd(i,k)可得:
igcd(i,k)j=kgcd(i,k)q(q为整数) \frac{i}{gcd(i,k)}j=\frac{k}{gcd(i,k)}q(q为整数)gcd(i,k)ij=gcd(i,k)kqq为整数)
根据最大公约数的定义,以下两数互质:
(igcd(i,k),kgcd(i,k))=1 (\frac{i}{gcd(i,k)} , \frac{k}{gcd(i,k)})=1(gcd(i,k)i,gcd(i,k)k)=1
假如a * bc的倍数,且a和c互质,可得b也是c的倍数,因为a * b / c是一个整数,而a与c相同的公因子只有1,因此b必须是c的整倍数,代入上式得出:
j=pkgcd(i,k)(p为整数)j = \frac{pk}{gcd(i,k)}(p为整数)j=gcd(i,k)pkp为整数)
即我们只需要遍历所有下标,对于每个下标i,找到i和k的最大公因数,然后用k除该最大公因数得到g,当j是g的倍数且满足nums[i]和nums[j]值相等时,就找到了一个符合题意的数对。我们可以先遍历一遍数组,找出以g为键,以g的每个整数倍下标对应的元素值及其个数为值的数组,遍历时到下标i时,我们已经计算出了g,只需要看数组中以g为键且元素值为nums[i]的元素个数即为下标i符合题意的数对个数:

class Solution {
public:
    int countPairs(vector<int>& nums, int k) {
        unordered_map<int, unordered_map<int, int>> factorAndNum;

        int maxFactor = nums.size();
        if (k + 1 < maxFactor) {
            maxFactor = k + 1;
        }
        // 此处为调和级数级时间复杂度O(nlgn)
        // 先找出所有可能的最大公因数对应的其整数倍下标中的值及该值的数量
        for (int i = 1; i < maxFactor; ++i) {
            for (int j = i; j < nums.size(); j += i) {
                ++factorAndNum[i][nums[j]];
            }
        }

        int ans = 0;
        for (int i = 0; i < nums.size(); ++i) {
            int factor = k / gcd(i, k);
            
            ans += factorAndNum[factor][nums[i]];
            // 此处需特殊处理0,因为上面预处理时按下标的倍数计算,0的任何倍数都是0
            if (nums[i] == nums[0]) {
                ++ans;
            }
			// 去掉i本身,如果i也是factor的倍数,说明被多计算了一次
            if (i % factor == 0) {
                --ans;
            }
        }
    	// 最后结果需要除2,因为nums[i]==nums[j]时,遍历到i和j时各计算了一次
        return ans / 2;
    }
};

如果输入数组长度为n,此算法时间复杂度为O(nlgn),空间复杂度为(nlgn)。

以上过程中,有一个结论:两个数分别除以这两数的最大公因数,所得的商互质。因为每个数都可以写成若干质数的乘积,最大公因数是取出两个质数池中的公共部分,两个集合中各自剩下的因数的积一定互质。如36=2*2*3*3,24=2*2*2*3,最大公因数为2*2*3=12,剩下的数分别为2和3,是互质的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值