题目一
题意:
给你一串数字,然后给你一个数aim,让你求累加和为aim的长度最长的子串的长度。我们规定数字可正,可负,可零
假如给你一串数字:1,0,0,0,0,7,-1,1,10,5
aim等于7,那么最长的子串的长度是7,这个子串是:0,0,0,0,7,-1,1。累加和为7
思路:
我们枚举所有位置,假设我们枚举到了i位置,我们求以i位置为结束位置的满足条件的子串,然后定义一个len变量,维护最长的子串的长度。
我们定义一个map,key代表累加和,value代表最早出现这个累加和的位置。定义一个sum代表累加和,我们假设现在累加到了200位置,累加和为100,aim为40,如果我们要找满足条件的子串,只需要找到i位置前面的位置最早出现累加和为60的位置就可以,然后用i位置减去这个累加和为60的最早的位置,就是累加和为40的满足条件的子串。
代码:
#include<algorithm>
#include<iostream>
#include<limits.h>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<map>
#include<set>
#define mod 1000000007
#define MAXN 1000001
typedef long long ll;
using namespace std;
int n,aim;
int root[MAXN];
int GetMaxLen()
{
int sum=0;
int len=0;
map<int,int>q;
q[0]=-1;//必须先往map中加入这条记录
for(int i=0;i<n;i++)
{
sum+=root[i];
if(q.find(sum-aim)!=q.end())//如果map中有sum-aim,说明i位置前面的某个位置累加和为sum-aim
len=max(len,i-q[sum-aim]);//更新len,q[sun-aim]代表i位置前面的位置最早出现累加和为sum-aim的位置
if(q.find(sum)==q.end())//如果sum这个和在map中没有出现,往map中加入这个值,如果map中存在了sum,就不需要加了
q[sum]=i;
}
return len;//最后返回这个len就好了
}
int main()
{
cin>>n>>aim;
for(int i=0;i<n;i++)
cin>>root[i];
cout<<GetMaxLen()<<endl;
}
上面题目的变形题目:
给你一串数字,让你求满足条件的最长的子串的长度,这个条件是字串奇数和偶数的个数相同。
思路:
我们将数字串中所有奇数变为-1,偶数变为1,然后求累加和为0的最长子串的长度,和上面的题目一模一样。
题目二:
题意:
给定一个数组arr,全是正数 ;一个整数aim,求累加和等于aim的,最长子数组,要求额外空间复杂度O(1),时间复杂度O(n)
思路:
我们运用窗口来做,直接举例说明,假设给定的数组arr为[1,2,5,3,4],aim等于8。
sum代表子数组的累加和,L,R代表窗口的左端点和右端点,len代表累加和等于aim的最长子数组的长度,初始时sum等于0,L,R都等于-1,我们遍历数组中的所有数,在遍历的过程中一共存在三种情况:
情况一:如果当前的sum小于aim,就让R往右移动一个单位,然后sum累加上arr[R]。
情况二:如果当前的sum大于aim,就让L往右移动一个单位,然后sum减去arr[L]。
情况三:如果当前的sum等于aim,我们就更新len,然后sum减去arr[L],然后L往右移动一个单位。
代码:
#include<algorithm>
#include<iostream>
#include<limits.h>
#include <sstream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#define mod 1000000007
#define MAXN 10001
typedef long long ll;
using namespace std;
int n,aim;
int arr[MAXN];
int GetMaxLen()
{
int len=0;
int l=-1,r=-1;
int sum=0;
for(int i=0;i<n;i++)
{
if(sum==aim)//情况三
{
len=max(len,r-l);
sum-=arr[l];
l++;
}
else if(sum<aim)//情况一
{
r++;
sum+=arr[r];
}
else//情况二
{
sum-=arr[l];
l++;
}
}
return len;
}
int main()
{
cin>>n>>aim;
for(int i=0;i<n;i++)
cin>>arr[i];
cout<<GetMaxLen()<<endl;
}
题目三:
题意:
给定一个数组arr,值可正,可负,可0;一个整数aim,求累加和小于等于aim的,最长子数组,要求时间复杂度O(n)
思路:
我们定义两个数组,
MaxnSum[ ]:MaxnSum[i]代表以i位置为开头的子数组的最小累加和为多少。
MaxnSumIndex[ ]:MaxnSumIndex[i]代表得到这个最小累加和到达的arr数组的位置。
如何求这两个数组,我们运用dp的思想,从数组的最后一位往前推,一共分为两种情况:
假设我们求MaxnSum[i]和MaxnSumIndex[i]
情况一:MaxnSum[i+1]位置上的数字>0: MaxnSum[i]=arr[i]
MaxnSumIndex[i]=i
情况二:MaxnSum[i+1]位置上的数字<=0: MaxnSum[i]=arr[i]+MaxnSum[i+1]
MaxnSumIndex[i]=MaxnSumIndex[i+1]
实现代码:
void GetMax()
{
for(int i=n-1;i>=0;i--)//从数组最后一个位置往前推
{
if(i==n-1||MaxnSum[i+1]>0)//情况一
{
MaxnSum[i]=arr[i];
MaxnSumIndex[i]=i;
}
else if(MaxnSum[i+1]<=0)//情况二
{
MaxnSum[i]=arr[i]+MaxnSum[i+1];
MaxnSumIndex[i]=MaxnSumIndex[i+1];
}
}
}
下面就是这个题目最精华的部分了:
我们首先定义L R sum变量。
sum:累加和.
L:累加和为sum的子数组的头位置.
R:累加和为sum的子数组的末位置.
第一步:首先求出满足累加和小于aim的,以arr数组0位置为开头的子数组,求出这个子数组的终止位置R和这个子数组的累加和sum(通过MaxnSum数组和MaxnSumIndex数组求)。
第二步:然后减去arr[L]位置,更新L(此时的sum=sum-arr[L])。
第三步:执行完第二步后,会分为两种情况:
情况一:sum+MaxnSum[R+1]小于aim,就更新sum和R,然后执行第二步。
情况二:sum+MaxnSum[R+1]大于aim,不更新,直接执行第二步。
最后,需要注意的是,我们要定义一个len变量来维护满足条件的子数组的长度,这个思路有很多边界需要自己coding,自己最好写一下。
代码:
#include<algorithm>
#include<iostream>
#include<limits.h>
#include <sstream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#define mod 1000000007
#define MAXN 10001
typedef long long ll;
using namespace std;
int n,aim;
int arr[MAXN];
int MaxnSum[MAXN];
int MaxnSumIndex[MAXN];
void GetMax()
{
for(int i=n-1;i>=0;i--)//从数组最后一个位置往前推
{
if(i==n-1||MaxnSum[i+1]>0)//情况一
{
MaxnSum[i]=arr[i];
MaxnSumIndex[i]=i;
}
else if(MaxnSum[i+1]<=0)//情况二
{
MaxnSum[i]=arr[i]+MaxnSum[i+1];
MaxnSumIndex[i]=MaxnSumIndex[i+1];
}
}
}
int GetMaxLen()//核心部分
{
int L=-1,R=0,len=0;//这就是我们定义的变量
int sum=0;
while (R<n)//这个while循环是第一步
{
sum+=MaxnSum[R];
if(sum>aim)
{
sum-=MaxnSum[R];
break ;
}
R=MaxnSumIndex[R]+1;
}
len=max(len,R-L-1);//始终维护len变量
while (R<n)//第二步和第三步
{
L++;
sum-=arr[L];//减去子数组的头位置
while (R<n)//继续尝试sum加上MaxnSum[R]是否小于aim
{
sum+=MaxnSum[R];
if(sum>aim)//情况二
{
sum-=MaxnSum[R];
break ;
}
//如果程序能够运行到这里,说明是情况一
R=MaxnSumIndex[R]+1;//这个地方是一个细节操作
}
len=max(len,R-L-1);//维护len变量
}
return len;//返回最长的子数组的长度
}
int main()
{
cin>>n>>aim;
for(int i=0;i<n;i++)
cin>>arr[i];
GetMax();
cout<<GetMaxLen()<<endl;
}
这个题目很难,思路上很难,代码coding上也不简单,最好自己亲自做一下。