算法通关村--滑动窗口高频问题

学习了滑动窗口,用几道滑动窗口经典问题,加深一下滑动窗口的学习。

最长字串专题

无重复字符的最长字串

LeetCode3:

给定一个字符串s,请你找出其中不含有重复字符的最长字串的长度。

示例1:

输入:s = “abcabcbb”

输出:3

解释:因为无重复字符的最长字串是“abc”,所以其长度为3。

示例2:

输入:s = “bbbbb”

输出:1

解释:因为无重复字符的最长字串是“b”,所以其长度为1。

示例3:

输入:s = “pwwkew”

输出:3

解释:因为无重复字符的最长字串是“wke”,所以其长度为3。请注意,你的答案必须是字串的长度,“pwke”是一个子序列,不是字串。

思路:

使用两个指针表示字符串中的某个字串的左右边界,其中左指针代表着枚举字串的起始位置,右指针表示窗口的边界

在每一步操作中,我们会将左指针向右移动一格,表示开始枚举下一个字符作起始位置,然后不断右移右指针,需要保证这两个指针对应的字串没有重复字符。移动结束后,这个字串就是对应着以左指针开始的,不包含重复字符的最长字串。记录这个字串的长度。

同时,用数据结构hash来判断时候是否有重复的字符,看集合中时候含有这个元素,如果有调整left的位置,如果没有就加入到集合中。

HashMap<Character,Integer> hashMap = new HashMap<>();
        int max = 0;
        int left = 0;
        for (int right = 0; right < s.length(); right++){
            //如果集合中有这个元素,调整left的位置
            if (hashMap.containsKey(s.charAt(right))){
                left = Math.max(left, hashMap.get(s.charAt(right)) + 1);
            }
            //如果集合中没有这个元素,加入到集合
            hashMap.put(s.charAt(right),right);
            max = Math.max(max,right - left + 1);
        }
        return max;

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

LeetCode159:

给定一个字符串s,找出至多包含两个不同字符的最长字串t,并返回该字串的长度。

输入:”eceba“

输出:3

解释:t是”ece“,长度为3。

思路:

如果输入的字符串s的长度小于等于2,那么它自己就是至多包含不同字符的最长子串。

right每往右走一步,都要去判断left和right区间字符串种类有没有超过两种

1.如果没有,说明left和right区间的字符串是符合要求的,更新长度max = Math.max(max,right-left)

2.如果超过了两种,则left往右移动,直到区间字符种类不超过2种为止,更新长度max = Math.max(max,right-left)

又该如何去解决区间内字符种类是否超过了2种?

可以用HashMap,key为字符,value为字符所在区间靠近left的那个索引。

1.map.put(s.charAt(right),right),如果s。charAt(right)已存在,则更新他的索引位置,不存在的,就添加进去。

2.判断map的元素是不是等于3个,如果是,就删除索引最小的那个元素。

public static int lengthOfLongestSubstringTwoDistinct(String s){
        //长度 <= 2,就返回字符串长度
        if (s.length() <= 2){
            return s.length();
        }
        int left = 0;
        int right = 0;
        int maxLength = 2;
        HashMap<Character,Integer> hashMap = new HashMap<>();
        while (right < s.length()){
            hashMap.put(s.charAt(right),right);
            right++;
            if (hashMap.size() == 3){
                int delIdx = Collections.min(hashMap.values());
                hashMap.remove(s.charAt(delIdx));
                left = delIdx + 1;
            }
            maxLength = Math.max(maxLength,right - left);
        }
        return maxLength;
    }

长度最小的子数组

LeetCode209:

给定一个含有n个正整数的数组和一个正整数target。

找出该数组中,满足其总和大于等于target的长度的最小的连续子数组[numsl,numsl+1,...,numsr-1,numsr],并返回其长度。如果不存在符合条件的子数组,返回0。

示例1:

输入:target = 7,nums =  [2,3,1,2,4,3]

输出:2

解释:子数组[4,3],是该条件下的长度最小的子数组。

示例2:

输入:target = 4,nums =  [1,4,4]

输出:1

示例3:

输入:target = 11,nums =  [1,1,1,1,1,1,1,1]

输出:0

思路:

定义两个指针left,right。sum来存储子数组的和。

每次移动,都将nums[right]加入到sum,如果sum大于s,则更新子数组的最小长度,然后将nums[left]从sum中移除,并将left右移,直到sum小于s,同样更新子数组最小长度。每一轮迭代,将right右移。

public static int minSubArrayLen(int[] nums,int target){
        int n = nums.length;
        int left = 0;
        int right = 0;
        int sum = 0;
        int minSize = Integer.MAX_VALUE;
        if (n == 0){
            return 0;
        }
        while (right < n){
            sum += nums[right];
            //和大于target,移除左边第一个元素,更新最小窗口;
            while (sum >= target){
                minSize = Math.min(minSize,right - left + 1);
                sum -= nums[left];
                left++;
            }
            right++;
        }
        return minSize == Integer.MAX_VALUE ? 0:minSize;
    }

盛水最多的容器

LeetCode11:

给定一个长度为n的整数数组height。有n条垂线,第 i 条线的两个端点(i,0)和(i,height[i])。

找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

示例1:

输入:[1,8,6,2,5,4,8,3,7]

输出:49

解释:图中垂线代表输入数组[1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)最大值为49。

示例2:

输入:height = [1,1]

输出:1

思路:

两个指针i,j分别指向水槽板的高h[i],h[j],此时水槽面积为s(i,j)。由于可容纳水的高度由两块板较低的一块决定。因此s(i,j) = min(h[i],h[j])*(j-i)

public static int maxArea(int[] height){
        int i = 0;
        int j = height.length - 1;
        int res = 0;
        while (i < j){
            res = height[i] < height[j] ?
                    Math.max(res,(j-i)*height[i++]):
                    Math.max(res,(j-i)*height[j--]);
        }
        return res;
    }

寻找字串异位词(排列)

字符串的排列

LeetCode567:

给你两个字符串s1和s2,写一个函数来判断s2是否包含s1的排列。如果是,返回true;否则,返回false。

换句话说,s1的排列之一是s2的字串。

示例1:

输入:s1 = “ab”,s2 = “eidbaooo”

输出:true

解释:s2包含s1的排列之一(“ba”)

示例2:

输入:s1 = “ab”,s2 = “eidboaoo”

输出:false

 思路:

由于排列不会改变字符串中每个字符的个数,所以只有当两个字符串每个字符的个数均相等时,一个字符串才是另一个字符串的排列。

记s1的长度为n,遍历s2中每个长度为n的字串,判断字串和s1中每个字符的个数是否相等,相等说明该字串是s1的一个排列。

使用两个数cnt1和cnt2,cnt1统计s1中各个字符的个数,cnt2统计当前遍历的字串中各个字符的个数。

使用一个长度为n的滑动窗口维护cnt2:滑动窗口每右滑依次,就多统计一次进入窗口的字符,少统计一次离开窗口的字符。判单cnt1和cnt2是否相等,若相等说明s1的排列之一是s2的字串。

public static boolean checkInclusion(String s1,String s2){
        int n = s1.length();
        int m = s2.length();
        if (n > m){
            return false;
        }
        int[] cnt1 = new int[26];
        int[] cnt2 = new int[26];
        for (int i = 0; i < n; ++i){
            ++cnt1[s1.charAt(i) - 'a'];
            ++cnt2[s2.charAt(i) - 'a'];
        }
        if (Arrays.equals(cnt1,cnt2)){
            return true;
        }
        for (int i = n; i < m; ++i){
            ++cnt2[s2.charAt(i) - 'a'];
            --cnt2[s2.charAt(i - n) - 'a'];
            if (Arrays.equals(cnt1,cnt2)){
                return true;
            }
        }
        return false;
    }

找到字符串中的所有字母异位

LeetCode438:

给定两个字符串s和p,找到s中所有p的异位词的字串,返回这些字串的起始索引。不考虑答案输出的顺序。

异位词指由相同字母重排列形成的字符串(包括相同的字符串)。

示例1:

输入:s = “cbaebabacd”,p = “abc”

输出:[0,6]

解释:起始索引等于0的子串是“cba”,它是“abc”的异位词。

起始索引等于6的子串是“bac”,它是“abc”的异位词。

示例2:

输入:s = “abab”,p = “ab”

输出:[0,1,2]ab

解释:起始索引等于0的子串是“a'b”,它是“ab”的异位词。

起始索引等于1的子串是“ba”,它是“ab”的异位词。

起始索引等于2的子串是“ab”,它是“ab”的异位词。

思路:

因为字符串s的异位词长度一定与字符串p的长度相同,所以可以构造一个与p长度相同的滑动窗口,当滑动窗口的字母数量和字符串每种字母数量相同时,说明当前窗口为字符串p的异位词。

public static List<Integer> findAnagrams(String s,String p){
        int sLen = s.length();
        int pLen = p.length();
        //s长度小于p,就返回空

        if (sLen < pLen){
            return new ArrayList<Integer>();
        }
        List<Integer> list = new ArrayList<>();
        int[] sCount = new int[26];
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; ++i){
            ++sCount[s.charAt(i) - 'a'];
            ++pCount[s.charAt(i) - 'a'];
        }
        if (Arrays.equals(sCount,pCount)){
            list.add(0);
        }
        for (int i = 0; i < sLen - pLen; ++i){
            --sCount[s.charAt(i) - 'a'];
            ++sCount[s.charAt(i + pLen) - 'a'];
            if (Arrays.equals(sCount,pCount)){
                list.add(i + 1);
            }
        }
        return list;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值