一、和相同的二元子数组
给你一个二元数组 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
将不会向用户 y
(x != 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;
}
}