思路,首先暴力肯定超时了,那么就要想办法来优化,
当然是百度了
首先很显然的是,如果一个区间的最大值和最小值分别是mi和ma
那么如果ma-mi<=limits,那么这个区间就是合法的区间
所以我们就要找一个方法快速的求区间的最大值与最小值
这当然可以用线段树来做,但是线段树写起来麻烦,此时祭出
大杀器,rmq,可以通过o(nlogn)复杂度来预处理,o(1)复杂度
查询区间的最值,有了这个之后,就可以二分答案,o(nlogn)来求解了
但是感觉双指针在这里应该也能做到,而且是o(n)但是影响不大了
RMQ的基本思想
RMQ的基本思想就是对区间的进行拆分,来利用动态规划的方法求解子问题
利用一个dp数组,其中dp[i][j]表示以i开始,长度为2^j次方的数组的最值
可以知道,这个数组是可以覆盖任何一个子区间的,因为数组的长度为n,那么
n必然可以拆分成2^k这种的相加形式,而且因为我们查询的时候,区间可以覆盖
所以一次查询只需要访问两次数组。而前面的预处理过程则由,大区间有小区间
覆盖来求得,方便快捷
int maxn[100005][20];
int minn[100005][20];
class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
//二分答案,求最大值与最小值
nums.insert(nums.begin(),0);
getbestarr(nums.size()-1,nums);
int ma = nums.size()-1;
int mi=1;
int mid=0;
int ans = 0;
int len=nums.size()-1;
while(mi<=ma){
bool flag = false;
mid = (ma+mi)>>1;
for(int i=1; i<=len-mid+1;i++){
if(abs(query(i,i+mid-1,0)-query(i,i+mid-1,1))<=limit){
flag=true;
ans = mid;
break;
}
}
if(flag){
mi=mid+1;
}
else{
ma=mid-1;
}
}
return ans;
}
void ini(){
for(int i=0; i<100005; i++){
for(int j=0; j<20; j++)
minn[i][0]= maxn[i][0] = 0;
}
}
void getbestarr(int n,vector<int>&arr)//n为给定的数组的长度
{
ini();
int tem = (int)floor(log2((double)n));
for(int i=1;i<=n;i++)
minn[i][0]= maxn[i][0] = arr[i];
for(int j=1;j<=tem;j++) //下标从1开始
for(int i=1;i+(1<<j)-1<=n;i++)
{
maxn[i][j] = max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]); //最大值
minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]); //最小值
}
}
int query(int a,int b,bool getwhat)//getwhat表示你是想取最大还是最小
{
int k = log2(b-a+1);
if(getwhat)
return max(maxn[a][k],maxn[b-(1<<k)+1][k]);
else
return min(minn[a][k],minn[b-(1<<k)+1][k]);
}
};