算法通关村第16关【白银】| 滑动窗口经典问题

1. 最长子串问题

(1)无重复字符的最长子串

思路:窗口内为无重复字符子串,使用哈希表来保存最新碰到的字符下标,只要出现重复就缩小left指针,缩小到无重复字符也就是map.get(c)+1

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashMap<Character,Integer> map = new HashMap<Character,Integer>();
        int l = 0;
        int r = 0;
        int len = s.length();
        int res = 0;
        while(r<len){
            char c = s.charAt(r);
            if(map.get(c) != null){
                l = Math.max(l,map.get(c) + 1);
            }
            map.put(c,r);
            res = Math.max(r-l+1,res); 
            r++;     
        }
        return res;
    }
}

(2)至多包含两个不同字符的最长子串

思路:窗口为两个以内的不同字符,使用哈希表来保存最新碰到的字符下标,和上一题不同的是这里需要关注哈希表的size,如果超过2代表窗口内有两个以上不同的字符了,需要更新left指针位置为记录的窗口内最小的位置,并移除

public static int lengthOfLongestSubstringTwoDistinct(String s) {

        if (s.length() < 3) {
            return s.length();
        }
        int left = 0, right = 0;
        HashMap<Character, Integer> hashmap = new HashMap<>();
        int maxLen = 2;

        while (right < s.length()) {

            if (hashmap.size() < 3)
                hashmap.put(s.charAt(right), right++);

            // 如果大小达到了3个
            if (hashmap.size() == 3) {
                // 最左侧要删除的位置
                int del_idx = Collections.min(hashmap.values());
                hashmap.remove(s.charAt(del_idx));
                // 窗口left的新位置
                left = del_idx + 1;
            }

            maxLen = Math.max(maxLen, right - left);
        }
        return maxLen;
    }

(3)至多包含k个不同字符的最长子串

题目:给定一个字符串 s,找出 至多 包含 k 个不同字符的最长子串T

思路:只需要将上一题的3改成k,完美移植

public static int lengthOfLongestSubstringKDistinct(String s, int k) {
        if (s.length() < k + 1) {
            return s.length();
        }

        int left = 0, right = 0;
        HashMap<Character, Integer> hashmap = new HashMap<>();
        int maxLen = k;

        while (right < s.length()) {

            if (hashmap.size() < k + 1)
                hashmap.put(s.charAt(right), right++);

            // 如果大小达到了k个
            if (hashmap.size() == k + 1) {
                //
                int del_idx = Collections.min(hashmap.values());
                hashmap.remove(s.charAt(del_idx));
                // 窗口left的新位置
                left = del_idx + 1;
            }

            maxLen = Math.max(maxLen, right - left);
        }
        return maxLen;
    }

2. 长度最小的子数组

思路:当小于target时就不断扩大窗口,当大于等于target的时候就从l开始不断剔除元素

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int l = 0;
        int r = 0;
        int sum = 0;
        int min = Integer.MAX_VALUE;
        while(r<nums.length){
            sum += nums[r];
            while(sum>=target){
                min = Math.min(min,r-l+1);
                sum -= nums[l++];
            }
            r++;
        }
        return min!=Integer.MAX_VALUE ? min : 0;
    }
}

3.盛水最多的容器

思路:这题和上面不同的地方在于,不是双指针从头开始慢慢增大窗口,而是从首尾开始缩小窗口

长板不动,因为长板往内移动是一定会变小容量的

短板内移,可能变大也可能变小还可能不变,遍历一遍取最大

class Solution {
    public int maxArea(int[] height) {
        int left = 0;
        int right = height.length-1;
        int low = Math.min(height[left],height[right]);
        int ability = 0;
        while(right > left){
            if(height[left]<height[right]){
                ability = Math.max(ability,(right-left)*height[left]);
                left++;
            }else{
                ability = Math.max(ability,(right-left)*height[right]);
                right--;
            }
        }
        return ability;
    }
}

4.寻找子串异位词

1)字符串的排列

思路:窗口为子串大小,从s1的位置为初始窗口位置开始遍历,看看s2窗口内是不是与s1相等的

class Solution {
    public boolean checkInclusion(String s1, String s2) {
        if(s1.length()>s2.length()){
            return false;
        }
       char[] c1 = new char[26];
       char[] c2 = new char[26];
       int left = 0;
       int right = s1.length();
        for(int i = 0;i<s1.length();i++){
            c1[s1.charAt(i)-'a']++;
            c2[s2.charAt(i)-'a']++;
        }
        if(Arrays.equals(c2,c1)){
            return true;
        }
        for(;right<s2.length();right++){
            c2[s2.charAt(right)-'a']++;
            c2[s2.charAt(left)-'a']--;
            left++;
            if(Arrays.equals(c2,c1)){
                return true;
            }
        }
        return false;

    }
}

2)找到字符串中所有字母异位词

思路:和上一题差不多,窗口为子串大小,从子串大小位置为初始窗口位置开始遍历

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        if(s.length()<p.length()){
            return new ArrayList<>();
        }
         char[] c1 = new char[26];
       char[] c2 = new char[26];
       int left = 0;
       int right = p.length();
        for(int i = 0;i<p.length();i++){
            c1[p.charAt(i)-'a']++;
            c2[s.charAt(i)-'a']++;
        }
        List<Integer> list = new ArrayList<>();
        if(Arrays.equals(c2,c1)){
            list.add(0);
        }
        while(right<s.length()){
            c2[s.charAt(right)-'a']++;
            c2[s.charAt(left)-'a']--;
             left++;
            if(Arrays.equals(c2,c1)){
                list.add(left);
            }
            right++;
        }
        return list;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值