题意:
给你一个长度为的01数组,你可以将其中个数字0变成数字1,问变化后数组中最长连续的一段1长度是多少?
解法一(暴力求解,不可AC)
首先我们可以枚举数组中每一个数,当枚举到第个数时,我们可以暴力求出以第个数为结尾,最长的连续一段1长度的值,我们记其为第个数对答案的贡献
最终答案即为所有贡献的最大值
具体的:
对于第个数字,我们可以暴力往前循环,若遇到0,则将减一;若遇到1,则继续向前,直到无法向前或者为0则结束循环
最后对于第个数字的贡献即为遇到数字的总个数
(注意每次要恢复为原来的值)
代码:
class Solution { public: int solve(int n, int m, vector<int>& a) { int ans=0;//最终答案 for(int i=0;i<n;i++){ int t=m;//临时存储m int c=0;//遇到的数字个数 for(int j=i;j>=0&&t>0;j--){ c++;//数字个数+1 if(a[j]==0)t--;//消耗一次变1的机会 } ans=max(ans,c);//答案取最大值 } return ans; } };时间复杂度:,枚举每一个数字的代价是的,对于每一个数字,往前循环计算贡献的代价也是的,故总的时间复杂度是
空间复杂度:,程序中需要存储个整型数字和若干个整型变量,故空间复杂度为
解法二(队列优化)
在解法一中,程序主要的时间瓶颈是对于每个数字计算贡献需要的时间
我们同样是考虑计算以第个数为结尾,最长的连续一段1长度的值
我们考虑解法一中往前枚举将0变成1的过程,0的位置一定是距离当前位置最近的个0所在的位置,这个位置我们可以用一个大小为的队列来维护
例如下图所示的例子:
我们设表示以第个元素为结尾,最长的连续的1的长度,显然有:
我们设表示若第个数字为1,以第个元素为结尾,最长的连续的1的长度,显然有:
我们假设队头元素为,显然由于队列存储的是距离当前位置最近的个0所在的位置,则后面的0一定变成了1,则当前以第个数为结尾,最长的连续一段1长度的值为
于是我们只需要预处理出数组,然后在枚举每一个数字的过程中维护上述队列即可,最后答案即为最大的以第个数为结尾,最长的连续一段1长度的值
代码:
class Solution { public: int f[1000000];//以第i个元素为结尾,最长的连续的1的长度 int g[1000000];//若第i个元素为1,最长的连续的1的长度 int solve(int n, int m, vector<int>& a) { memset(f,0,sizeof f); memset(g,0,sizeof g);//多测清空 f[0]=a[0]; g[0]=1; for(int i=1;i<a.size();i++){ if(a[i]){ f[i]=f[i-1]+a[i]; }else{ f[i]=0; } g[i]=max(f[i],f[i-1]+1); } queue<int> que; int ans=a[0];//表示最终答案 for(int i=1;i<n;i++){ if(!a[i]){ //若当前数字为0 if(que.size()==m)que.pop();//队列大小最大只能为m que.push(i); if(m>1){ ans=max(ans,i-que.front()+g[que.front()]); }else{ //若只能改变一个0,则当前位置对答案的贡献为g[i] ans=max(ans,g[i]); } }else{ if(que.size()>0){ ans=max(ans,i-que.front()+g[que.front()]); }else{ ans=max(ans,f[i]); } } } return ans;//返回答案 } };时间复杂度:,我们预处理出数组的代价是,枚举每一个数字的代价是的,由于每一个元素最多只会进出队列一次,故维护队列的复杂度为,求解每一个位置的贡献的代价为,故总的时间复杂度为
空间复杂度:,数组的都是占级别的内存,队列的大小最多为,故总的空间复杂度为