滑动窗口系列(三指针)9/11

一、和相同的二元子数组

给你一个二元数组 nums ,和一个整数 goal ,请你统计并返回有多少个和为 goal 的 非空 子数组。子数组 是数组的一段连续部分。

输入:nums = [1,0,1,0,1], goal = 2
输出:4
解释:
有 4 个满足题目要求的子数组:[1,0,1]、[1,0,1,0]、[0,1,0,1]、[1,0,1]
思路:

使用滑动窗口的思路来做,题目中要求goal=2的子数组。我们可以通过计算出goal<=2和goal<2的子数组,然后将两个相减。

goal<=2的:

        while(right<nums.length){
            sum1+=nums[right];
            while(left1<=right&&sum1>goal){
                sum1-=nums[left1++];
            }
            count1+=right-left1+1;
            right++;
        }

goal<=2的:

        while(right<nums.length){
            sum2+=nums[right];
            while(left2<=right&&sum2>=goal){
                sum2-=nums[left2++];
            }
            count2+=right-left2+1;
            right++;
        }

代码:
/**
三指针:
公用一个右指针,然后根据不同的条件分别移动左指针。
 */
class Solution {
    public int numSubarraysWithSum(int[] nums, int goal) {
        int left1=0;//<=的
        int left2=0;//<的
        int right=0;
        int count1=0;
        int count2=0;
        int sum1=0;
        int sum2=0;
        while(right<nums.length){
            sum1+=nums[right];
            sum2+=nums[right];
            while(left1<=right&&sum1>goal){
                sum1-=nums[left1++];
            }
            count1+=right-left1+1;
            while(left2<=right&&sum2>=goal){
                sum2-=nums[left2++];
            }
            count2+=right-left2+1;
            right++;
        }
        return count1-count2;
    }
}

二、模板

两个左指针公用一个右指针;左指针移动的条件不相同,一个当xx>=,一个当xx>;然后计算出差值,就是==的情况。

class Solution {
    public int numberOfSubarrays(int[] nums, int k) {
        int left1 = 0, left2 = 0, right = 0;
        int count1 = 0, count2 = 0;
        int res1 = 0, res2 = 0;
        while (right < nums.length) {
            if (条件) {
                count1++;
                count2++;
            }
            while (left1<=right&&不满足条件就移动左指针) {
                if (满足条件的话)
                    count1--;
                left1++;
            }
            //收割结果
            res1 += right - left1 + 1;
            while(left2<=right&&不满足条件就移动左指针){
                if(满足条件的话){
                    count2--;
                }
                left2++;
            }
            //收割结果
            res2+=right-left2+1;
            right++;
        }
        return res1-res2;
    }
}

三、将数组分成三个子数组的方案数

我们称一个分割整数数组的方案是 好的 ,当它满足:

  • 数组被分成三个 非空 连续子数组,从左至右分别命名为 left , mid , right 。
  • left 中元素和小于等于 mid 中元素和,mid 中元素和小于等于 right 中元素和。

给你一个 非负 整数数组 nums ,请你返回 好的 分割 nums 方案数目。由于答案可能会很大,请你将结果对 109 + 7 取余后返回。

思路:

要满足left<=mid<=right;我们可以使用三指针+前缀和的思路;

1.首先计算出nums的前缀和数组:nums

2.然后通过for循环枚举左边分界点,左边分界点固定之后,寻找中间分界点的范围。

    2.1 首先寻找最大的中间分界点:需要满足右边的区域大于等于中间的区域

while(r<n&&sum[n]-sum[r]>=sum[r]-sum[i]){
    r++;
}

    2.2寻找最小的中间分界点:需要满足左边的区域小于等于中间的区域

while(l<n&&sum[l]-sum[i]<=sum[i]){
    l++;
}

    2.3 找到一个中间分界点的区域范围(l,r)之后, 就能收割结果了。

if(l<r)res=(res+l-r)%MOD
代码:
class Solution {
    public int waysToSplit(int[] nums) {
        //1.计算前缀和
        int n=nums.length;
        int[] sum=new int[n+1];
        long res=0;
        final int MOD=1000000000+7;
        for(int i=1;i<=n;i++){
            sum[i]=sum[i-1]+nums[i-1];//代表的是前i个元素的和 下标是从0->i-1
        }
        //2.开始找
        for(int i=1,l=2,r=2;i<n;i++){
            //首先更新l,r 不能小于左边分界点 (l,r)是中间分界点的范围
            l=Math.max(l,i+1);
            r=Math.max(r,i+1);
            //然后找中间分界点的最大值
            while(r<n&&sum[n]-sum[r]>=sum[r]-sum[i]){
                r++;
            }
            //找中间分界点的最小值
            while(l<n&&sum[l]-sum[i]<sum[i]){
                l++;
            }
            if(l<r){
                res=(res+r-l)%MOD;
            }
        }
        return (int)res%MOD;
    }
}

四、适龄的朋友
 

在社交媒体网站上有 n 个用户。给你一个整数数组 ages ,其中 ages[i] 是第 i 个用户的年龄。

如果下述任意一个条件为真,那么用户 x 将不会向用户 yx != y)发送好友请求:

  • ages[y] <= 0.5 * ages[x] + 7
  • ages[y] > ages[x]
  • ages[y] > 100 && ages[x] < 100

否则,x 将会向 y 发送一条好友请求。

示例 2:

输入:ages = [16,17,18]
输出:2
解释:产生的好友请求为 17 -> 16 ,18 -> 17 。
思路:

总结一下,要想使x向y发送好友需求,需要满足三个条件:

1.x的年龄大于y:ages[x]>ages[y]

2. 0.5*ages[x]+7<=ages[y]

3.x的年龄小于100时,y的年龄不能大于100

第一个要点:所以:0.5*ages[x]+7<=ages[y]<ages[x] 因此x必须大于14

第二个要点:遍历每一个年龄,然后确定一个范围(他可以向哪些年龄的人发送请求的范围)

利用left、right双指针去确定。

            while(left<ages.length&&ages[left]<=0.5*age+7){
                left++;
            }

            while(right<ages.length&&ages[right]<=age){
                right++;
            }

代码:
class Solution {
    public int numFriendRequests(int[] ages) {
        Arrays.sort(ages);
        int res=0;
        int left=0;
        int right=0;
        for(int age:ages){
            if(age<15)continue;
            while(left<ages.length&&ages[left]<=0.5*age+7){
                left++;
            }
            while(right<ages.length&&ages[right]<=age){
                right++;
            }
            if(left<right)res+=right-left-1;
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值