news 2026/5/4 12:54:43

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

作者头像

张小明

前端开发工程师

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

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完整代码如下:

packagemainimport("fmt""sort")funcminStable(nums[]int,maxCint)int{n:=len(nums)leftMin:=make([]int,n)varleft,bottom,rightGcdintfori,x:=rangenums{rightGcd=gcd(rightGcd,x)forleft<=i&&gcd(nums[left],rightGcd)==1{ifbottom<=left{// 重新构建一个栈// 由于 left 即将移出窗口,只需计算到 left+1forj:=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(upperint)bool{c:=maxC i:=upperfori<n{ifi-leftMin[i]+1>upper{ifc==0{returnfalse}c--i+=upper+1}else{i++}}returntrue})returnans}funcgcd(a,bint)int{fora!=0{a,b=b%a,a}returnb}funcmain(){nums:=[]int{3,5,10}maxC:=1result:=minStable(nums,maxC)fmt.Println(result)}

Python完整代码如下:

# -*-coding:utf-8-*-importmathfromtypingimportListimportbisectdefminStable(nums:List[int],maxC:int)->int:n=len(nums)leftMin=[0]*n left=0bottom=0rightGcd=0foriinrange(n):x=nums[i]rightGcd=math.gcd(rightGcd,x)whileleft<=iandmath.gcd(nums[left],rightGcd)==1:ifbottom<=left:# 重新构建一个栈# 由于 left 即将移出窗口,只需计算到 left+1forjinrange(i-1,left,-1):nums[j]=math.gcd(nums[j],nums[j+1])bottom=i rightGcd=0left+=1leftMin[i]=left# 二分查找defcheck(upper:int)->bool:c=maxC i=upperwhilei<n:ifi-leftMin[i]+1>upper:ifc==0:returnFalsec-=1i+=upper+1else:i+=1returnTrue# 使用二分查找找到最小满足条件的值low,high=0,n//(maxC+1)+1whilelow<high:mid=(low+high)//2ifcheck(mid):high=midelse:low=mid+1returnlow# 另一种更简洁的二分查找实现方式defminStable_alternative(nums:List[int],maxC:int)->int:n=len(nums)leftMin=[0]*n left=0bottom=0rightGcd=0foriinrange(n):x=nums[i]rightGcd=math.gcd(rightGcd,x)whileleft<=iandmath.gcd(nums[left],rightGcd)==1:ifbottom<=left:# 重新构建一个栈forjinrange(i-1,left,-1):nums[j]=math.gcd(nums[j],nums[j+1])bottom=i rightGcd=0left+=1leftMin[i]=left# 使用 Python 的 bisect 风格二分查找ans=bisect.bisect_left(range(n//(maxC+1)+1),True,key=lambdaupper:check(upper))defcheck(upper:int)->bool:c=maxC i=upperwhilei<n:ifi-leftMin[i]+1>upper:ifc==0:returnFalsec-=1i+=upper+1else:i+=1returnTruereturnans# 测试代码if__name__=="__main__":nums=[3,5,10]maxC=1result=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),]fornums,maxCintest_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>usingnamespacestd;intgcd(inta,intb){while(a!=0){inttemp=a;a=b%a;b=temp;}returnb;}intminStable(vector<int>&nums,intmaxC){intn=nums.size();vector<int>leftMin(n,0);intleft=0,bottom=0,rightGcd=0;for(inti=0;i<n;i++){intx=nums[i];rightGcd=gcd(rightGcd,x);while(left<=i&&gcd(nums[left],rightGcd)==1){if(bottom<=left){// 重新构建一个栈// 由于 left 即将移出窗口,只需计算到 left+1for(intj=i-1;j>left;j--){nums[j]=gcd(nums[j],nums[j+1]);}bottom=i;rightGcd=0;}left++;}leftMin[i]=left;}// 二分查找autocheck=[&](intupper)->bool{intc=maxC;inti=upper;while(i<n){if(i-leftMin[i]+1>upper){if(c==0){returnfalse;}c--;i+=upper+1;}else{i++;}}returntrue;};intlow=0,high=n/(maxC+1)+1;while(low<high){intmid=low+(high-low)/2;if(check(mid)){high=mid;}else{low=mid+1;}}returnlow;}// 使用C++标准库的binary_search风格intminStable2(vector<int>&nums,intmaxC){intn=nums.size();vector<int>leftMin(n,0);intleft=0,bottom=0,rightGcd=0;for(inti=0;i<n;i++){intx=nums[i];rightGcd=gcd(rightGcd,x);while(left<=i&&gcd(nums[left],rightGcd)==1){if(bottom<=left){// 重新构建一个栈for(intj=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(inti=0;i<range.size();i++){range[i]=i;}autoit=lower_bound(range.begin(),range.end(),true,[&](intupper,bool){intc=maxC;inti=upper;while(i<n){if(i-leftMin[i]+1>upper){if(c==0){returntrue;// 返回true表示upper不够大}c--;i+=upper+1;}else{i++;}}returnfalse;// 返回false表示upper足够大});return(it!=range.end())?*it:n;}intmain(){vector<int>nums={3,5,10};intmaxC=1;intresult=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;intmaxC=testCase.second;intresult=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;}return0;}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 10:02:30

HunyuanVideo-Foley与MoFos内容平台结合?探讨合法应用场景边界

HunyuanVideo-Foley与内容平台的融合&#xff1a;智能音效的边界与可能 在短视频日均产量突破千万条的今天&#xff0c;一个看似微小却影响深远的问题浮出水面&#xff1a;为什么那么多视频听起来“干巴巴”的&#xff1f; 答案并不复杂——音效制作太贵、太慢、太专业。传统…

作者头像 李华
网站建设 2026/5/2 12:30:05

小白前端必看:5种CSS图片垂直居中方案(附实战技巧+避坑指南)

小白前端必看&#xff1a;5种CSS图片垂直居中方案&#xff08;附实战技巧避坑指南&#xff09;小白前端必看&#xff1a;5种CSS图片垂直居中方案&#xff08;附实战技巧避坑指南&#xff09;引言&#xff1a;一张图如何逼疯一群前端先搞清楚&#xff1a;浏览器到底怎么算“居中…

作者头像 李华
网站建设 2026/5/3 19:26:05

DOCX.js前端Word文档生成完整指南:零服务器依赖的终极解决方案

DOCX.js前端Word文档生成完整指南&#xff1a;零服务器依赖的终极解决方案 【免费下载链接】DOCX.js Generate Microsoft Word DOCX files in pure client-side JavaScript. Try in Chrome 项目地址: https://gitcode.com/gh_mirrors/do/DOCX.js 在当今数据驱动的时代&a…

作者头像 李华
网站建设 2026/4/25 19:41:55

wiliwili跨平台B站客户端终极使用指南:从入门到精通

wiliwili作为专为手柄控制设计的第三方跨平台B站客户端&#xff0c;目前可以运行在PC全平台、PSVita、PS4和Nintendo Switch上。这款开源应用以其独特的操作体验和全面的功能支持&#xff0c;为用户带来了全新的B站观看方式。无论您是游戏设备爱好者还是PC用户&#xff0c;本指…

作者头像 李华
网站建设 2026/4/30 4:54:00

高效会议管理:3步告别冗长低效,拯救你的会议时间

高效会议管理&#xff1a;3步告别冗长低效&#xff0c;拯救你的会议时间行业痛点分析当前&#xff0c;会议服务领域正面临着一系列深刻的技术与管理挑战。随着混合办公模式的普及与会议复杂度的提升&#xff0c;传统会议模式在效率、协同及成果转化方面的短板日益凸显。首要痛点…

作者头像 李华