【算法】【优选算法】滑动窗口(下)


一、904.⽔果成篮

题目链接:904.⽔果成篮
题目描述:

题目解析:

  • 这道题给的题目挺长,但是简化后就是给你一个数组,在其中找到数组元素只有两个数的最长子串。
  • 提示中写了数组元素的范围是小于等于数组长度的。

1.1 滑动窗口

解题思路:

  • 当我们遍历数组的时候,我们要让子数组中元素种类变多,就让子数组尾向后走;
  • 让子数组中元素种类变少,就让子数组头向后走;这又是同向双指针问题。
  • 我们还要使用一个计数器来标记子数组中的元素种类个数。
  • 使用一个type数组来标记子数组中两个种类中的个数,以便后面对计数器操作。
  • 进窗口:当元素种类个数小于等于2的时候,就让type数组中fruits[right]下标的元素加一,并让right向后走,这里面要注意如果是第一次进type数组,要让计数器加一。
  • 出窗口条件:当计数器大于2的时候就出窗口。
  • 出窗口:每次让type数组中的fruits[left]下标的元素加一,并让left向后走,这里面要注意如果是fruits[left]下标对应type数组元素为0,要让计数器减一。
  • 更新结果:在出完窗口后的子数组一定是符合条件的,直接去原结果和子数组长度的较大值即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(n)
import java.util.*;
class Solution {
   
    public int totalFruit(int[] fruits) {
   
        int len = fruits.length;
        int[] type = new int[len]; 
        int ret = 0;
        int flag = 0;
        for(int left = 0, right = 0; right < len; right++) {
   
            if(flag <= 2) {
   
                if(type[fruits[right]] == 0) {
   
                    flag++;  
                } 
                type[fruits[right]]++ ;//进窗口

            }

            while(flag > 2) {
   //出窗口条件
                //出窗口
                type[fruits[left]]--;
                if( type[fruits[left]] == 0) flag--;
                left++;
            }
            ret = Math.max(ret,right - left + 1);//更新结果
        }
        return ret;
    }
}

1.2 暴力枚举

解题思路:

  • 使用两层for循环将每一个符合要求的子数组都枚举出来。
  • 我们也要使用一个计数器来标记子数组中的元素种类个数。
  • 使用一个type数组来标记子数组中两个种类中的个数,以便后面对计数器操作。
  • 这里面有细节处理问题:我们出第二层循环的时候有两种可能:
    • 我们数组遍历完了,并且计数器小于等于2,出循环这时的[ i, j ]都是符合要求的。
    • 我们计数器大于二出循环,这时只有[ i, j )是符合要求的(也就是j下标执向的是第三种类型的元素)。
  • 会超时。

解题代码:

//时间复杂度:O(n^2)
//空间复杂度:O(n)
import java.util.*;
class Solution {
   
    public int totalFruit(int[] fruits) {
   
        int len = fruits.length;
        int ret = 0;
        for(int i = 0; i < len; i++) {
   
            int[] type = new int[len];
            int flag = 0;
            for(int j = i; j < len; j++) {
   
                if(flag <= 2) {
   
                    if(type[fruits[j]] == 0) flag++;
                    type[fruits[j]]++;
                }
                if(j == len - 1 && flag <= 2) {
   
                    ret = Math.max(ret,j-i+1);
                    break;
                }
                if(flag > 2) {
   
                    ret = Math.max(ret,j-i);
                    break;
                }
            }
        }    
        return ret;
    }
}

二、438.找到字符串中所有字⺟异位词

题目链接:438.找到字符串中所有字⺟异位词
题目描述:

题目解析:

  • 字母异位词:其实就是将字符串中所有字母随机排序能构成的所有子串都互为异位词,如abc的异位词就有:abc,acb,bac,bca,cab,cba。
  • 这道题就是让我们求给出字符串s中子串,是字符串p的异位词的首字母下标构成的集合。
  • 两个字符串是不是异位词的判定方法:
    • 将两个字符串排序,然后遍历一一比较。Java提供的sort时间复杂度O(NlogN)。
    • 哈希原理:使用数组分别记录下字符串中每个字符的个数,比较数组是否相等即可。

2.1 滑动窗口

解题思路:
-当我们遍历字符串的时候,在保持子串的元素尽可能少的变化(进出字符的时候中间的字符不会变化),要让子串的长度变大,就让子串尾向后走,让子串的长度变小,就让子串头向后走,同向双指针。

  • 记录下p字符串的长度lenP,一个数组hash1记录每个字符个数。
  • 一个数组hash2记录当前子串每个字符个数。
  • 进窗口:当子串长度小于lenP的时候,进字符。
  • 出窗口条件:当子串长度不小于lenP的时候。
  • 出窗口:让left下标对应字符出数组(也就是hash2对应下标减1)。
  • 修改结果:在每次出窗口后都要看现在的子串长度是否等于lenP,和两个数组是否一样。一样就将当前的left进入结果数组。
  • 上述的思路最后由于我们需要比较数组所以我们的复杂度虽然是O(N)但是,每一次比较都有26次。我们可以使用下述优化方案:
    • 我们使用一个计数器,来统计有效字符的个数(也就是与p字符串中字符相同的,子串中字符个数)。
    • 当我们进窗口之后,当前hash2数组right下标的个数小于等于hash1中的值时,就计数器加1。
    • 当出窗口之前,当前hash2数组left下标的个数小于等于hash1中的值时,就计算器减1。
    • 最后只需要比较计数器与p的长度比较即可。

解题代码:

//时间复杂度:O(n)
//空间复杂度:O(1)
import java.util.*;
class Solution {
   
    
评论 117
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸽鸽程序猿

蟹蟹大哥

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值