04.子串,启动!

子串

  • 子串要求连续,与子序列不同,子序列不要求连续。

和为k的字串

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数。
子数组是数组中元素的连续非空序列

class Solution {
    public int subarraySum(int[] nums, int k) {
        int n = nums.length;
        int[] sums = new int[n];
        sums[0] = nums[0];
        for (int i = 1; i < n; i++) {
            sums[i] = sums[i-1] + nums[i];
        }
        HashMap<Integer, Integer> map= new HashMap<>();
        map.put(0,1);
        int count = 0;
        for (int i = 0; i < n; i++) {
            count+=map.getOrDefault(sums[i]-k,0);//map.getOrDefault若查询失败会返回默认值而不是null
            map.put(sums[i],map.getOrDefault(sums[i],0)+1);
        }
        return count;
    }
}

滑动窗口最大值

  • 方法
    1.维护一个单调递减栈,即栈底为当前栈内最大的元素,且保证栈内的元素一定在当前遍历的窗口内;
    2.窗口每向后移动一次,首先要维护栈内的递减性,然后还要将左侧超出窗口外的元素在栈中移除。
    将窗口外的元素移除,分为两种情况:
    a.要移除的是当前最大值,则我们要pollLast(),移除栈底
    b.要移除的不是当前最大值,说明在之前已经被弹出过。
    换句话说,我们可以判断当前栈底元素下标是否在窗口内(deque.peekLast() <= (i-k)),若在则deque.pollLast(),否则直接跳过。
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        if (n == 0) {
            return new int[0];
        }
        int[] res = new int[n - k + 1];
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            //保证栈内从栈顶到栈底为递增的(first为栈顶)
            //peek == peekFirst
            while (!deque.isEmpty() && nums[i] > nums[deque.peek()]) {
                deque.pop();//pop == pollFirst
            }
            deque.push(i); //push == addFirst
            if(i>=k){
                //移除窗口外的元素
                if(deque.peekLast() <= (i-k)){
                    deque.pollLast();
                }
            }
            if (i >= k - 1) {
                //记录结果
                res[i - k + 1] = nums[deque.peekLast()];
            }
        }
        return res;

    }
}

最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。

  • 分析
    前面已经做过几道关于异位词的题目,若只关心字符串的原材料,我们可以利用一个数组来描述一个字符串的信息。
    此处若s 中的子串涵盖 t 所有字符,则该子串的每个原材料数量一定不小于t。
  • 方法
    维护一个窗口,右边界一直向后扩张,扩张到子串的每个原材料数量都不小于t,即包含t,然后再从左侧缩小,每缩小一次就判断子串是否仍然包含t,直至缩小至不包含,为避免后续有更短的包含t的子串,要继续向后扩张至包含t,再从左侧缩小,在这个同时要维护一对最终结果的左右边界,直至左边界或右边界越界
class Solution {
    public String minWindow(String s, String t) {
        int[] count_t = count(t);
        int[] count_s = new int[128];
        int l = 0;
        int r = -1;
        int l_res = 0;
        int r_res = Integer.MAX_VALUE;
        while(l < s.length()){
            if(has(count_s,count_t)){
                if(r-l < r_res-l_res){
                    l_res = l;
                    r_res = r;
                }
                count_s[s.charAt(l)]--;
                l++;
            }else {
                if(r==s.length()-1){
                    break;
                }
                r++;
                count_s[s.charAt(r)]++;
            }
        }
        return r_res==Integer.MAX_VALUE ? "" : s.substring(l_res,r_res+1);
    }
    public int[] count(String s){
        int[] counts = new int[128];
        char[] chars = s.toCharArray();
        for (char c: chars) {
            counts[c]++;
        }
        return counts;
    }
    public boolean has(int[] s,int[] t){
        for (int i = 0; i < 128; i++) {
            if(s[i]<t[i]){
                return false;
            }
        }
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值