1、给出一个序列,求连续子串和的最大值,要求在O(n)求解
思路:积分图+两层for循环遍历起始位置和终止位置,O(n^2)
最优思路:依次累加,并不断更新Max,在累加的过程中,若sum小于0,更新开始位置。
其实这个更新位置我纠结很久,这里若第i位sum<0,sum从下一位开始累加求和。第i要是为正数怎么办?怎么可能,sum(i-1)+a[i]<0,sum(i-1)要求是大于0的,那么a[i]必定是负数。
int fun1(vector<int> a,int n){ //长度>=1
int sum=0;
int Max=-999;
for(int i=0;i<n;i++){
sum+=a[i];
if(sum>Max) Max=sum;
if(sum<0) sum=0;
}
return Max;
}
2、给出一个序列,求连续子串和的最大值的最大长度,要求在O(n)求解
思路:这个可以在最大连续子序列和的基础上进行操作,注意:最大长度,sum>=Max,等于也要判断是否出现MaxP,还要最重要的约束条件是,sum必须是小于0的时候才能更新位置。这样才能符合长度最长。(若最小长度,只需要在最大Max时候去最小的MaxP,同时sum<=0的时候进行位置更换,才符合最短长度)
int fun2(vector<int> a,int n){
int sum=0;
int Max=-999;
int MaxP=0;
int p=0;
for(int i=0;i<n;i++){
sum+=a[i];
p+=1;
if(sum>=Max) {
Max=sum;
if(p>MaxP) MaxP=p;
}
if(sum<0) sum=0,p=0;
}
return p;
}
3、给出一个序列,求连续子串和的最大值,长度要求大于等于2
思路:单独拿出来一个数据,保证至少每次两个数据以上,难点在于位置的更新,在小于0的时候位置更新毫无疑问,但是这个还是远远不够的,若sum(i)=sum(i-1)+a[i]<a[i],虽然和是大于0,但是sum(i-1)小于0,也需要更换位置。如测试数据,-1+8也需要移动位置。
int fun3(vector<int> a,int n){ //长度>=2
int sum=a[0];
int Max=-999;
for(int i=1;i<n;i++){
sum+=a[i];
if(sum>Max) Max=sum;
if(sum<0||sum<a[i]) { //-1,8,-1,-5,10
sum=a[i];
}
}
return Max;
}
4、给出一个序列,只有0和1组成,求0和1个数相等的最长子串。
5、给出一个序列,只有-1和1组成,求和为0的最长子串。
思路:O(n)的时间复杂度,利用Map(sum,index),统计和为某个值得时候最小下标。然后后面出现相同sum时,位置做差。4和5是同一道题,把0变成-1即可。
int fun4(vector<int> a,int n){
map<int,int>m; //和,位置
int sum=0;
int Max=-999;
for(int i=0;i<n;i++){
sum+=a[i];
if(sum==0) Max=Max>i+1?Max:i+1;
if(m[sum]!=0){ //bug,记录坐标为0,会有冲突,map没有默认也为0
Max=Max>(i-m[sum]+1)?Max:(i-m[sum]+1);
}else {
m[sum]=i+1; //记录第一次位置
}
}
return Max;
}
6、给出一个序列,求和为k的序列个数(看似背包问题,实则不是,区别:连续,不重复)
思路:上一个是求最长长度,这一个是求最大个数。都是map辅助,累和为下标记录个数或者位置,进行操作。前者是记录位置,出现相同的sum时,位置做差记录Max(注意求最长,记录第一次的即可)。后者是记录个数,当出现和为sum时,进行加一操作,代表和为sum的序列个数。若sum-k存在,那么其中的值就是和为sum-k的序列个数,那么sum和sum-k做差也就有相同的序列个数(差刚好为k),注意特殊形式,sum==k时候,sum-k肯定不存在,但是全序列刚好为k,也就是一种情况。
int fun5(vector<int> a,int n,int k){
map<int,int>m; //和,个数
int sum=0;
int ans=0;
for(int i=0;i<n;i++){
sum+=a[i];
if(sum==k) ans+=1;
if(m[sum-k]!=0){ //两个差为k
ans+=m[sum-k];
}
m[sum]+=1; //记录个数
}
return ans;
}