最小覆盖子串-Java版本

package com.areio.suanfa.zifuchuan;

/*最小覆盖子串
 * 给定两个字符串S和T,在S中找出包含T中全部字母的最短子串,如果S中没有这个子串,则返回空字符串,如果存在这样一个字符串,则可认为答案是唯一的
 *
 * S="EBBANCF",T="ABC"  返回BANC
 * 第一次窗口扩大到valid=need.size()是子串为EBBANC,此时left=0,right=6,窗口区间[0,6)
 * 而need={A=1,B=1,C=1},window={A=1,B=2,C=1} window中三个字符都已满足期待出现的次数,所以valid=3
 *
 *
 * */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/*
 * s是给定的字符串
 * t是目标字符串
 *
 * 快慢指针在数组上的应用
 * */

/*注意:
*1、在扩大和缩小窗口的时候要维护valid
*2、在缩小窗口的时候要更新最终结果
*
* */
public  class minWindow {
    public static String minWindow(String s,String t){
        //两个字符计数器,分布统计每个字符出现的次数,key是字符,val是出现次数
        //其中need代表目标字符串每个字符需要出现次数,window统计在当前窗口中出现目标字符串中每个字符的次数
        Map<Character,Integer> need=new HashMap<>();
        Map<Character,Integer> window=new HashMap<>();
        //统计目标字符串中每个字符需要出现的次数,比如t=“abc”,那么need={a=1,b=1,c=1}
        char[] tArray = t.toCharArray();
        for (char c: tArray) {
            //添加或者覆盖某键值对
            //need.getOrDefault(c,0)代表如果该key存在,则取key对应的值,不存在则得到默认值0
            need.put(c,need.getOrDefault(c,0)+1);
        }
        char[] sArray = s.toCharArray();
        //设置快慢指针的初始位置,此时窗口[left,right)=[0,0)为空
        int left=0,right=0;
        //valid代表满足要求的字符个数,比如need={a=1,b=2} 而window={a=1,b=1}那么只有a达到预期次数,此时valid=1
        int valid=0;
        //记录最小覆盖子串的起始索引及其长度
        int start=0,len=Integer.MAX_VALUE;

        while(right<s.length()){
            //将要加入到窗口的字符
            char c=sArray[right];
            //扩大窗口
            right++;
            //更新窗口内的数据
            if (need.containsKey(c)){
                //如果need需要这个key,就在统计窗口中出现该字符的次数
                window.put(c,window.getOrDefault(c,0)+1);
                //如果窗口中出现该字符的次数等于目标子串中需要这个字符的次数
                if(window.get(c)==need.get(c)){
                    valid++;
                }
            }
            //是否要开始缩小窗口;条件是一旦need中所有的字符都满足了期待值,那么就立马开始收缩,一旦窗口大小缩小到开始不满足need中所有的字符有一个不满足期待出现的次数,即窗口中所表示的字符串没有包含T中全部的字符时
            while (valid==need.size()){
                //长度相等之外,还需满足窗口中出现每个字符串的个数是否与目标字符串每个字符所需出现的次数一致
                 /* S="EBBANCF",T="ABC"  返回BANC
                  * 第一次窗口扩大到valid=need.size()是子串为EBBANC,此时left=0,right=6,窗口区间[0,6)
                  * 而need={A=1,B=1,C=1},window={A=1,B=2,C=1} window中三个字符都已满足期待出现的次数,所以valid=3
                  * */
                if (right-left<len){
                    //更新最小覆盖子串,一旦进入循环就代表当前这个子串是有覆盖目标子串,但是未必是最小的
                    start=left;
                    len=right-left;

                }
                //将要被踢出窗口的字符
                char d=sArray[left];
                //缩小窗口
                left++;
                //更新窗口内的数据
                if (need.containsKey(d)){

                    //如果need需要这个key,且恰好这个字符在窗口中出现的次数等于它需要出现的次数,那么缩小完 这个字符出现的次数-1,必然满足条件的字符就会-1
                    if (window.get(d)==need.get(d)){
                        valid--;
                    }
                    window.put(d,window.getOrDefault(d,0)-1);
                }
            }
            //重复扩大窗口和缩小窗口的过程
        }
        return len==Integer.MAX_VALUE?"":s.substring(start,start+len);
    }

    public static void main(String[] args) {
        String result=minWindow("EBBANCF","ABC");
        System.out.println(result);
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值