76.最小覆盖子窜

97 篇文章 0 订阅
70 篇文章 0 订阅

76.最小覆盖子窜

解题思路:双指针滑动窗口

package leadcode;

import java.util.HashMap;
import java.util.Map;

/**
 * @author : icehill
 * @description : 最小覆盖子窜
 * 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果s中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
 * 注意:如果s中存在这样的子串,我们保证它是唯一的答案。
 * 示例 1:
 * 输入:s = "ADOBECODEBANC", t = "ABC"
 * 输出:"BANC"
 * 示例 2:
 * 输入:s = "a", t = "a"
 * 输出:"a"
 * 提示:
 * 1 <= s.length, t.length <= 105
 * s 和 t 由英文字母组成
 * 进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗?
 * <p>
 * 来源:力扣(LeetCode)
 * 链接:https://leetcode-cn.com/problems/minimum-window-substring
 * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
 * 题目分析:
 * 需要找到s的子窜,并且子窜必须包含t中所有的字符,且字符出现的频率必须大于t
 * 例如s=abcb t=aab,s中无法覆盖t
 * 1.暴力解法(不考虑)
 * 枚举s所有长度大于t的子窜,判断子窜是否覆盖t
 * 时间复杂度:O(n^3) 空间复杂度O(n) (n为s的长度,求s的子窜,n^2,对于子窜,再判断频数跟t比较n,所以总的n^3)
 * 由于子窜的判断中做了很多重复的工作,或者没必要的工作,所以我们不考虑这种解法
 * 2.我们使用双指针的滑动窗口解题
 * 例如:s=aadobecodebanc t=abc
 * a.移动右指针找到第一个覆盖t
 * s=[aadobec]odebanc
 * b.移动左指针,直到不满足覆盖t
 * s=a[adobec]odebanc
 * c.右指针向右移动并探测,如果找到一个属于t的字符,重复b尝试移动右指针,记录当前窗口是否是最小值
 * s=a[adobecodeb]anc
 * s=aadobe[codeba]nc
 * s=aadobecode[banc]
 * d.返回最小子窜
 * @date : 2021-05-18
 */
public class Solution76 {
    public static void main(String[] args) {
        Solution76 solution76 = new Solution76();
//        System.out.println(solution76.minWindow("ADOBECODEBANC", "ABC"));
        System.out.println(solution76.minWindow("bba", "ab"));
    }

    public String minWindow(String s, String t) {
        Map<Character, Integer> tMap = new HashMap<>();
        Map<Character, Integer> targetMap = new HashMap<>();
        //把t转换成哈希表,并统计数量
        for (int i = 0; i < t.length(); i++) {
            if (!tMap.containsKey(t.charAt(i))) {
                tMap.put(t.charAt(i), 1);
                targetMap.put(t.charAt(i), 0);
            } else {
                tMap.put(t.charAt(i), 1 + tMap.get(t.charAt(i)));
            }
        }
        int count = 0;
        int start = -1;
        int end = -1;
        //移动右指针,直到找到第一个符合的子窜
        for (int j = 0; j < s.length(); j++) {
            if (tMap.containsKey(s.charAt(j))) {
                if (start == -1) {
                    start = j;
                }
                targetMap.put(s.charAt(j), 1 + targetMap.get(s.charAt(j)));
                if (targetMap.get(s.charAt(j)) <= tMap.get(s.charAt(j))) {
                    count++;
                }
            }
            if (count >= t.length()) {
                end = j;
                break;
            }
        }
        if (count != t.length()) {
            return "";
        }
        //移动左指针,直到刚好满足覆盖子窜,就是当前右指针所在位置对应的最小子窜
        while (start < end + 1) {
            if (targetMap.get(s.charAt(start)) == null) {
                start++;
                continue;
            }
            if (targetMap.get(s.charAt(start)) > tMap.get(s.charAt(start))) {
                targetMap.put(s.charAt(start), targetMap.get(s.charAt(start)) - 1);
                start++;
            } else {
                break;
            }
        }
        int slow = start;
        //移动右指针,扩大滑动窗口
        for (int fast = end + 1; fast < s.length(); fast++) {
            if (tMap.containsKey(s.charAt(fast))) {
                targetMap.put(s.charAt(fast), 1 + targetMap.get(s.charAt(fast)));
                //移动左指针,缩小滑动窗口
                while (slow < fast) {
                    if (targetMap.get(s.charAt(slow)) == null) {
                        slow++;
                        continue;
                    }
                    if (targetMap.get(s.charAt(slow)) > tMap.get(s.charAt(slow))) {
                        targetMap.put(s.charAt(slow), targetMap.get(s.charAt(slow)) - 1);
                        slow++;
                    } else {
                        break;
                    }
                }
                if (fast - slow < end - start) {
                    start = slow;
                    end = fast;
                }
            }
        }
        return s.substring(start, end + 1);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值