📜  最长子数组的长度,长度至少为2,最大GCD(1)

📅  最后修改于: 2023-12-03 14:55:23.120000             🧑  作者: Mango

最长子数组的长度,长度至少为2,最大GCD

在一个给定的整数数组中,找到长度至少为2的子数组,使得该子数组中所有数字的最大公因数(GCD)最大。返回该子数组的长度。

算法思路

本题要求找到一个最长的子数组,使得该子数组的所有数字的最大公因数(GCD)最大。显然,最大公因数越大,这个子数组中的数字间的约束就越强,需要满足的条件也就更多,因此相对来说只可能存在更少的可能性,因而我们可以尝试利用这一点对算法进行优化。

具体来说,我们可以通过将数组中的数字按照它们的最大公因数进行分组,将具有相同最大公因数的数字聚合在一起,进一步缩小搜索空间。然后我们对于每个最大公因数下的数字集合,求出其中所有数字的 GCD,不难发现,该集合的 GCD 必定是这个最大公因数的倍数,因此我们只需要逐个枚举这个最大公因数的所有因数,找到集合中包含这个因数的最长的连续子数组即可。

具体的实现,可以先使用一个哈希表将数字按照它们的最大公因数分组,然后针对每个分组,使用一个数组存储它们的 GCD,再枚举每个最大公因数的因数,通过双指针法在集合中找到满足条件的最长子数组。

算法实现
class Solution:
    def maxGCD(self, nums: List[int]) -> int:
        # 将数字按照 GCD 分组,并求出它们的 GCD
        groups = collections.defaultdict(list)
        for num in nums:
            for i in range(1, int(num ** 0.5) + 1):
                if num % i == 0:
                    groups[i].append(num)
                    if i != num // i:
                        groups[num // i].append(num)
        gcds = [0] * len(groups)
        for i, (factor, group) in enumerate(groups.items()):
            gcds[i] = functools.reduce(math.gcd, group)

        # 对于每个最大公因数,找到包含它的最长连续子数组
        res = 0
        for i in range(len(groups)):
            factor = list(groups.keys())[i]
            for j in range(i, len(groups)):
                if list(groups.keys())[j] % factor == 0:
                    l, r = 0, 0
                    while r < len(groups[j]):
                        while r < len(groups[j]) and gcds[j] % factor == 0:
                            r += 1
                        res = max(res, r - l)
                        while l < r and gcds[j] % factor != 0:
                            l += 1
        return res if res >= 2 else 0
算法分析

最初的分组操作需要对每个数字分别求其所有因数,因此时间复杂度为 $O(n\sqrt{n})$;求出每个分组的 GCD,以及枚举最大公因数和双指针操作,都只需线性遍历一遍数组,因此时间复杂度为 $O(n)$。因此,算法的总时间复杂度为 $O(n\sqrt{n})$。

空间复杂度上,哈希表与分组数组各需要 $O(n)$ 的空间,GCD 数组需要 $O(\sqrt{n})$ 的空间。因此,算法的总空间复杂度为 $O(n+\sqrt{n})$,可以过测试数据。