📅  最后修改于: 2023-12-03 14:58:17.323000             🧑  作者: Mango
给定一个由正整数组成的序列,找到其中长度至少为 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),可以通过验证。