📜  长度至少为 2 且具有最大 GCD 的最长子阵列的长度(1)

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

长度至少为 2 且具有最大 GCD 的最长子阵列的长度

简介

给定一个由正整数组成的序列,找到其中长度至少为 2 且具有最大 GCD 的最长子序列的长度。

解法
算法思路

我们可以枚举所有可能的子序列并计算 GCD,但是这种方法的时间复杂度是 O(N^3),无法通过本题目。我们需要一种更快的算法。

我们可以利用一个结论:在一个数组中,整除关系的传递性可以使得最长公共前缀在连续块中。也就是说,一个子序列的最大 GCD 一定是这个序列中某一段的 GCD。

于是,我们可以遍历所有可能的 GCD,对于每个 GCD,使用双指针维护一个区间,满足区间中的所有元素都能被 GCD 整除。我们需要找到的是一个尽量长的区间,因此我们只需要让右指针不断向右移动,同时维护 GCD,直到出现一个元素不能被整除为止。此时,当前区间的长度就是一个可能的答案。我们可以维护所有可能的答案,然后取最大值即可。

时间复杂度

外层循环枚举 GCD,时间复杂度为 O(N log N)。内层循环使用双指针维护区间,时间复杂度为 O(N)。因此,总时间复杂度为 O(N^2 log N)。

代码
def max_gcd_subsequence_length(nums: List[int]) -> int:
    ans = 0
    for gcd in range(max(nums), 1, -1):
        left, right = 0, 0
        while right < len(nums):
            if nums[right] % gcd != 0:
                left = right + 1
            right += 1
            if right - left > ans:
                ans = right - left
    return ans
int max_gcd_subsequence_length(vector<int>& nums) {
    int ans = 0;
    for (int gcd = *max_element(nums.begin(), nums.end()); gcd > 1; gcd--) {
        int left = 0, right = 0;
        while (right < nums.size()) {
            if (nums[right] % gcd != 0) {
                left = right + 1;
            }
            right++;
            if (right - left > ans) {
                ans = right - left;
            }
        }
    }
    return ans;
}
public static int maxGcdSubsequenceLength(int[] nums) {
    int ans = 0;
    for (int gcd = Arrays.stream(nums).max().orElse(1); gcd > 1; gcd--) {
        int left = 0, right = 0;
        while (right < nums.length) {
            if (nums[right] % gcd != 0) {
                left = right + 1;
            }
            right++;
            if (right - left > ans) {
                ans = right - left;
            }
        }
    }
    return ans;
}
总结

本题目的关键在于将一个序列的 GCD 的计算转化为遍历可能的 GCD,然后利用双指针维护区间,找到满足条件的最长区间。该算法的时间复杂度为 O(N^2 log N),可以通过验证。