力扣【76】最小覆盖子串

题目:

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。

 

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:

输入:s = "a", t = "a"
输出:"a"

题解:

小知识:批量修改变量名操作:shift+F6选中变量--->修改变量--->Enter回车标签:。

labuladong的框架:

滑动窗口框架(子字符串匹配问题)  

int left = 0, right = 0;

while (right < s.size()) {
    window.put(s[right],window.getOrDefault(s[right], 0) + 1);
    right++;
    
    while (valid == need.size()) {
        window.put(s[left],window.get(s[left]) - 1);
        left++;
    }
}

方法一:按照框架的做法:

import java.util.*;
class Solution {
    public String minWindow(String s, String t) {
        // 为什么用HashMap, 不用HashSet?
        // 因为子串中可能有重复字符, 需要以k,v的形式, 记录每个字符的出现次数
        // 整个过程中, needs只初始化一次, window中的元素一直在变化
        HashMap<Character, Integer> needs = new HashMap<>();
        HashMap<Character, Integer> window = new HashMap<>();

        // 初始化needs, 作为后面判断窗口是否符合条件的依据
        for (int i = 0; i < t.length(); i++) {
            needs.put(t.charAt(i), needs.getOrDefault(t.charAt(i), 0) + 1);
        }

        int left = 0, right = 0;
        // valid代表有多少个字符满足条件了, 如果vaild == needs.size(), 就认为凑满了
        int valid = 0;
        // 记录最小覆盖字串的起始索引及长度
        int start = 0, len = Integer.MAX_VALUE;

        // 右指针超出尾部就算结束
        while (right < s.length()) {

            // 右指针移动
            char c1 = s.charAt(right);
            // 判断取出的字符是否在字串中
            if (needs.containsKey(c1)) {
                window.put(c1, window.getOrDefault(c1, 0) + 1);

                // 是否凑满need中的一个字符: 一个字符在need中出现了n次, 如果window中也累计了n次, 就认为凑满了
                if (window.get(c1).equals(needs.get(c1))) {
                    valid++;
                }
            }
            right++;

            // 判断是否需要收缩(已经找到合适的覆盖串)
            while (valid == needs.size()) {
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }

                char c2 = s.charAt(left);
                left++;
                if (needs.containsKey(c2)) {
                    window.put(c2, window.getOrDefault(c2, 0) - 1);
                    // 如果不满足needs中的元素要求
                    if (window.get(c2) < (needs.get(c2))) {
                        valid--;
                    }
                }

            }
        }

        return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
    }
}

public class Main {
    public static void main(String[] args) {
        String s = "ADOBECODEBANC";
        String t = "ABC";
        Solution solution = new Solution();
        String res = solution.minWindow(s, t);
        System.out.println("结果:" + res);
    }
}

方法二:

滑动窗口,使用数组来初始化每一个字符出现的次数。然后使用滑动窗口寻找包含字符串的最短字符串。

import java.util.*;
class Solution {
    public String minWindow(String s, String t) {
        if (s == null || t == null || s.length() == 0 || t.length() == 0) return "";
        // 定义一个数字,用来记录字符串 t 中出现字符的频率,也就是窗口内需要匹配的字符和相应的频率
        int[] map = new int[128];
        for (char c : t.toCharArray()) {
            map[c]++;
        }
        int left = 0, right = 0;
        int match = 0;  // 匹配字符的个数
        int minLen = s.length() + 1;   // 最大的子串的长度
        // 子串的起始位置 子串结束的位置(如果不存在这样的子串的话,start,end 都是 0,s.substring 截取就是 “”
        int start = 0, end = 0;
        while (right < s.length()){
            char charRight = s.charAt(right); // 右边界的那个字符
            map[charRight]--;   // 可以理解为需要匹配的字符 charRight 减少了一个
            // 如果字符 charRight 在 t 中存在,那么经过这一次操作,只要个数大于等于 0,说明匹配了一个
            // 若字符 charRight 不在 t 中,那么 map[charRight] < 0, 不进行任何操作
            if (map[charRight] >= 0) match++;
            right++;  // 右边界右移,这样下面就变成了 [),方便计算窗口大小

            // 只要窗口内匹配的字符达到了要求,右边界固定,左边界收缩
            while (match == t.length()){
                int size = right - left;
                if (size < minLen){
                    minLen = size;
                    start = left;
                    end = right;
                }
                char charLeft = s.charAt(left);  // 左边的那个字符
                map[charLeft]++;  // 左边的字符要移出窗口
                // 不在 t 中出现的字符,移出窗口,最终能够达到的最大值 map[charLeft] = 0
                // 如果恰好移出了需要匹配的一个字符,那么这里 map[charLeft] > 0, 也就是还要匹配字符 charLeft,此时 match--
                if (map[charLeft] > 0) match--;
                left++;  // 左边界收缩
            }
        }
        return s.substring(start, end);
    }
}

public class Main {
    public static void main(String[] args) {
        String s = "ADOBECODEBANC";
        String t = "ABC";
        Solution solution = new Solution();
        String res = solution.minWindow(s, t);
        System.out.println("结果:" + res);
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值