2025-12-16:数组的最小稳定性因子。用go语言,给定一个整数数组 nums 和一个整数 maxC。把满足以下条件的连续区

举报
福大大架构师每日一题 发表于 2025/12/16 06:36:37 2025/12/16
【摘要】 2025-12-16:数组的最小稳定性因子。用go语言,给定一个整数数组 nums 和一个整数 maxC。把满足以下条件的连续区间称为“稳定子数组”:区间内所有数的最大公约数(GCD)至少为 2。定义数组的“稳定性因子”为其最长稳定子数组的长度。你可以最多对数组中的 maxC 个位置改成任意整数。问题是:在不超过 maxC 次修改后,能够把稳定性因子降到多小?返回这个最小可能的稳定性因子;如...

2025-12-16:数组的最小稳定性因子。用go语言,给定一个整数数组 nums 和一个整数 maxC。把满足以下条件的连续区间称为“稳定子数组”:区间内所有数的最大公约数(GCD)至少为 2。

定义数组的“稳定性因子”为其最长稳定子数组的长度。你可以最多对数组中的 maxC 个位置改成任意整数。问题是:在不超过 maxC 次修改后,能够把稳定性因子降到多小?返回这个最小可能的稳定性因子;如果最终不存在任何稳定子数组,则返回 0。

补充说明:

  • 子数组指数组的连续片段。

  • 数组的最大公约数指能同时整除所有元素的最大整数。

  • 长度为 1 的子数组若其唯一元素 ≥ 2,也视为稳定子数组。

1 <= n == nums.length <= 100000。

1 <= nums[i] <= 1000000000。

0 <= maxC <= n。

输入:nums = [3,5,10], maxC = 1。

输出:1。

解释:

稳定的子数组 [5, 10] 的 HCF = 5,其稳定性因子为 2。

由于 maxC = 1,一个最优策略是将 nums[1] 改为 7,得到 nums = [3, 7, 10]。

现在,没有长度大于 1 的子数组的 HCF >= 2。因此,最小可能稳定性因子是 1。

题目来自力扣3605。

🔍 算法步骤详解

  1. 初始化与预处理

    • 函数首先获取数组长度n,并初始化一个与nums等长的切片leftMin。这个切片用于记录一个关键信息:对于每个位置ileftMin[i]表示以i为区间右端点时,能够使得区间内所有元素的最大公约数(GCD)大于等于2的、最靠左的起点位置。
    • 变量leftbottom初始化为0,它们共同维护一个滑动窗口或处理区间。rightGcd初始化为0(因为任何数与0的GCD是其本身)。
  2. 计算 leftMin 数组

    • 这是算法的核心步骤。它通过一次遍历(下标i从0到n-1)来计算leftMin数组。
    • 更新GCD:对于当前元素nums[i],代码通过rightGcd = gcd(rightGcd, nums[i])来累积计算从某个起点到i的整个区间的GCD。
    • 收缩左边界:使用一个内层循环(for left <= i && gcd(nums[left], rightGcd) == 1)来调整左边界left。这个条件的目的是检查当前窗口[left, i]的GCD是否因为包含了nums[left]而变为1(即不满足稳定子数组条件)。如果变为1,就需要向右移动left指针,直到窗口内的GCD再次大于等于2,或者窗口为空。
    • 记录左边界:将最终确定的左边界left记录到leftMin[i]中。
    • 栈式重建(关键优化):当需要移动left指针,并且bottom(可以理解为上一次重建的起始点)小于或等于当前的left时,会触发一个重建操作。这个操作从i-1left+1逆序地重新计算区间GCD,这类似于维护一个隐式的栈结构,用于高效地更新GCD信息,避免每次都从头计算。完成后,bottom被更新为irightGcd重置为0。
  3. 二分查找最小稳定性因子

    • 在得到leftMin数组后,算法使用二分搜索来确定最小的稳定性因子(即最长稳定子数组的长度上限upper)。
    • 搜索范围:二分查找的下界是0,上界可以设为n/(maxC+1),这是一个合理的估计,因为修改maxC个点最多能将数组分成maxC+1段。
    • 检查函数:对于每一个候选的upper值,函数模拟修改操作,检查是否能在最多maxC次修改后,使得数组中所有稳定子数组的长度都不超过upper
    • 贪心模拟:检查过程采用贪心策略。从左到右遍历数组,当遇到一个稳定子数组的长度超过upper时(即i - leftMin[i] + 1 > upper),就在这个子数组的末尾之后进行一次“修改”(实际上是通过将索引i跳转upper + 1位来模拟),并消耗一次修改次数。如果修改次数在遍历完数组前用完,则说明当前upper值太小,需要增大;否则,说明upper值可行,可以尝试更小的值。
  4. 返回结果

    • 二分查找结束后,找到的最小满足条件的upper值就是题目要求的最小可能稳定性因子,将其返回。

⏱ 复杂度分析

时间复杂度

  • 计算leftMin数组:主循环遍历每个元素一次。虽然内部有循环和重建操作,但每个元素最多被加入和移出“栈”一次,GCD计算可以看作是常数时间(因为整数大小有限,GCD操作很快)。因此,这部分的时间复杂度可以看作是 O(n)
  • 二分查找与检查:二分查找的迭代次数是 O(log n)。每次检查需要遍历整个数组,是 O(n)。所以这部分的总时间复杂度是 O(n log n)
  • 综合:整个算法的总时间复杂度为 O(n log n)

空间复杂度

  • 算法主要使用了leftMin数组,其大小为 O(n)
  • 其他变量如left, bottom, rightGcd等占用常数空间。
  • 在计算GCD的递归或栈模拟过程中,没有使用与输入规模成比例的额外数据结构。
  • 因此,算法的总额外空间复杂度为 O(n)

Go完整代码如下:

package main

import (
	"fmt"
	"sort"
)

func minStable(nums []int, maxC int) int {
	n := len(nums)
	leftMin := make([]int, n)
	var left, bottom, rightGcd int
	for i, x := range nums {
		rightGcd = gcd(rightGcd, x)
		for left <= i && gcd(nums[left], rightGcd) == 1 {
			if bottom <= left {
				// 重新构建一个栈
				// 由于 left 即将移出窗口,只需计算到 left+1
				for j := i - 1; j > left; j-- {
					nums[j] = gcd(nums[j], nums[j+1])
				}
				bottom = i
				rightGcd = 0
			}
			left++
		}
		leftMin[i] = left
	}

	ans := sort.Search(n/(maxC+1), func(upper int) bool {
		c := maxC
		i := upper
		for i < n {
			if i-leftMin[i]+1 > upper {
				if c == 0 {
					return false
				}
				c--
				i += upper + 1
			} else {
				i++
			}
		}
		return true
	})
	return ans
}

func gcd(a, b int) int {
	for a != 0 {
		a, b = b%a, a
	}
	return b
}

func main() {
	nums := []int{3, 5, 10}
	maxC := 1
	result := minStable(nums, maxC)
	fmt.Println(result)
}

在这里插入图片描述

Python完整代码如下:

# -*-coding:utf-8-*-

import math
from typing import List
import bisect

def minStable(nums: List[int], maxC: int) -> int:
    n = len(nums)
    leftMin = [0] * n
    
    left = 0
    bottom = 0
    rightGcd = 0
    
    for i in range(n):
        x = nums[i]
        rightGcd = math.gcd(rightGcd, x)
        
        while left <= i and math.gcd(nums[left], rightGcd) == 1:
            if bottom <= left:
                # 重新构建一个栈
                # 由于 left 即将移出窗口,只需计算到 left+1
                for j in range(i - 1, left, -1):
                    nums[j] = math.gcd(nums[j], nums[j + 1])
                bottom = i
                rightGcd = 0
            left += 1
        
        leftMin[i] = left
    
    # 二分查找
    def check(upper: int) -> bool:
        c = maxC
        i = upper
        while i < n:
            if i - leftMin[i] + 1 > upper:
                if c == 0:
                    return False
                c -= 1
                i += upper + 1
            else:
                i += 1
        return True
    
    # 使用二分查找找到最小满足条件的值
    low, high = 0, n // (maxC + 1) + 1
    while low < high:
        mid = (low + high) // 2
        if check(mid):
            high = mid
        else:
            low = mid + 1
    
    return low

# 另一种更简洁的二分查找实现方式
def minStable_alternative(nums: List[int], maxC: int) -> int:
    n = len(nums)
    leftMin = [0] * n
    
    left = 0
    bottom = 0
    rightGcd = 0
    
    for i in range(n):
        x = nums[i]
        rightGcd = math.gcd(rightGcd, x)
        
        while left <= i and math.gcd(nums[left], rightGcd) == 1:
            if bottom <= left:
                # 重新构建一个栈
                for j in range(i - 1, left, -1):
                    nums[j] = math.gcd(nums[j], nums[j + 1])
                bottom = i
                rightGcd = 0
            left += 1
        
        leftMin[i] = left
    
    # 使用 Python 的 bisect 风格二分查找
    ans = bisect.bisect_left(range(n // (maxC + 1) + 1), True, key=lambda upper: check(upper))
    
    def check(upper: int) -> bool:
        c = maxC
        i = upper
        while i < n:
            if i - leftMin[i] + 1 > upper:
                if c == 0:
                    return False
                c -= 1
                i += upper + 1
            else:
                i += 1
        return True
    
    return ans

# 测试代码
if __name__ == "__main__":
    nums = [3, 5, 10]
    maxC = 1
    result = minStable(nums, maxC)
    print(f"minStable result: {result}")
    
    # 测试更多用例
    test_cases = [
        ([3, 5, 10], 1),
        ([2, 3, 4, 5], 2),
        ([1, 2, 3, 4, 5], 1),
        ([6, 10, 15], 0),
    ]
    
    for nums, maxC in test_cases:
        result = minStable(nums.copy(), maxC)
        print(f"nums={nums}, maxC={maxC} -> result={result}")

在这里插入图片描述

C++完整代码如下:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>

using namespace std;

int gcd(int a, int b) {
    while (a != 0) {
        int temp = a;
        a = b % a;
        b = temp;
    }
    return b;
}

int minStable(vector<int>& nums, int maxC) {
    int n = nums.size();
    vector<int> leftMin(n, 0);

    int left = 0, bottom = 0, rightGcd = 0;

    for (int i = 0; i < n; i++) {
        int x = nums[i];
        rightGcd = gcd(rightGcd, x);

        while (left <= i && gcd(nums[left], rightGcd) == 1) {
            if (bottom <= left) {
                // 重新构建一个栈
                // 由于 left 即将移出窗口,只需计算到 left+1
                for (int j = i - 1; j > left; j--) {
                    nums[j] = gcd(nums[j], nums[j + 1]);
                }
                bottom = i;
                rightGcd = 0;
            }
            left++;
        }
        leftMin[i] = left;
    }

    // 二分查找
    auto check = [&](int upper) -> bool {
        int c = maxC;
        int i = upper;
        while (i < n) {
            if (i - leftMin[i] + 1 > upper) {
                if (c == 0) {
                    return false;
                }
                c--;
                i += upper + 1;
            } else {
                i++;
            }
        }
        return true;
    };

    int low = 0, high = n / (maxC + 1) + 1;
    while (low < high) {
        int mid = low + (high - low) / 2;
        if (check(mid)) {
            high = mid;
        } else {
            low = mid + 1;
        }
    }
    return low;
}

// 使用C++标准库的binary_search风格
int minStable2(vector<int>& nums, int maxC) {
    int n = nums.size();
    vector<int> leftMin(n, 0);

    int left = 0, bottom = 0, rightGcd = 0;

    for (int i = 0; i < n; i++) {
        int x = nums[i];
        rightGcd = gcd(rightGcd, x);

        while (left <= i && gcd(nums[left], rightGcd) == 1) {
            if (bottom <= left) {
                // 重新构建一个栈
                for (int j = i - 1; j > left; j--) {
                    nums[j] = gcd(nums[j], nums[j + 1]);
                }
                bottom = i;
                rightGcd = 0;
            }
            left++;
        }
        leftMin[i] = left;
    }

    // 使用lower_bound风格的二分查找
    vector<int> range(n / (maxC + 1) + 1);
    for (int i = 0; i < range.size(); i++) {
        range[i] = i;
    }

    auto it = lower_bound(range.begin(), range.end(), true,
                          [&](int upper, bool) {
                              int c = maxC;
                              int i = upper;
                              while (i < n) {
                                  if (i - leftMin[i] + 1 > upper) {
                                      if (c == 0) {
                                          return true; // 返回true表示upper不够大
                                      }
                                      c--;
                                      i += upper + 1;
                                  } else {
                                      i++;
                                  }
                              }
                              return false; // 返回false表示upper足够大
                          });

    return (it != range.end()) ? *it : n;
}

int main() {
    vector<int> nums = {3, 5, 10};
    int maxC = 1;
    int result = minStable(nums, maxC);
    cout << "minStable result: " << result << endl;

    // 测试更多用例
    vector<pair<vector<int>, int>> testCases = {
                                                {{3, 5, 10}, 1},
                                                {{2, 3, 4, 5}, 2},
                                                {{1, 2, 3, 4, 5}, 1},
                                                {{6, 10, 15}, 0},
                                                };

    for (auto& testCase : testCases) {
        vector<int> numsCopy = testCase.first;
        int maxC = testCase.second;
        int result = minStable(numsCopy, maxC);
        cout << "nums=[";
        for (size_t i = 0; i < testCase.first.size(); i++) {
            cout << testCase.first[i];
            if (i < testCase.first.size() - 1) cout << ", ";
        }
        cout << "], maxC=" << maxC << " -> result=" << result << endl;
    }

    return 0;
}

在这里插入图片描述

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。