LeetCode(76):最小覆盖子串 Minimum Window Substring(Java)

234 篇文章 1 订阅
177 篇文章 0 订阅

2019.6.8 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

字符串题大多数可以依靠动态规划或者分治思想解决。这道题的解题过程中可以学到一种动态的模板匹配的方法。

算法整体思路是滑动窗口:

  1. 在窗口元素和模板元素不匹配时,移动窗口右侧指针end扩大窗口,直到窗口元素与模板元素匹配。

  2. 在窗口元素正好匹配窗口元素时,移动窗口左侧指针begin压缩窗口,直到获得能够保持匹配的最小窗口。记录窗口起止点。

  3. 匹配的最小窗口两端必定为恰能满足条件的模板元素,此时将窗口左侧指针begin前移一位,删去该关键元素,窗口回到不匹配状态,再次重复上述过程直到窗口移动至最右即可。

在窗口移动的过程中动态检查模板匹配的算法:

定义一个存储模板元素出现次数的数组template,一个存储窗口元素出现次数的数组match(可以用HashMap记录,因为本题限定在52个字母,为计算方便,直接定义数组长度58,用于存储ASCII表中从‘A’-‘z’的58个字符),最后再定义一个记录窗口中模板元素出现总数的变量num。

  1. 数组读入时,创建并记录template。

  2. 移动窗口,实时更新match。当match中的元素小于template中的元素时,更新num(match中元素大于等于template时不更新)

  3. 当num与模板元素个数相等时,说明窗口元素必满足(或大于)模板元素,开始进行窗口压缩(删去多余的模板元素与其他非模板元素)。

  4. 缩小后的窗口存在一个特征:窗口两端元素为关键元素,删去后窗口必恰好不满足匹配,删去的同时更新match和num。重复上述步骤即可。

用测试用例模拟算法过程:

对于S = “ADOBECODEBANC”, T = “ABC”。初始时template为A1B1C1。

  1. 窗口滑动至"ADOBEC"时(实际用头尾指针记录),match中模板元素A1B1C1,num=3。由于已经是最小匹配窗口,因此无法压缩,记录下"ADOBEC"。窗口前移成"DOBEC",继续滑动。

  2. 窗口滑动至"DOBECODEB"时,match中模板元素A0B1C1,num=2,由于match中的B元素已经等于template中的B元素,因此num不再增加,继续滑动窗口。

  3. 窗口滑动至"DOBECODEBA"时,match中模板元素A1B1C1,num=3。压缩窗口至"CODEBA",记录窗口。前移成"ODEBA",继续滑动。

  4. 窗口滑动至"ODEBANC"时,match中模板元素A1B1C1,num=3。压缩窗口至"BANC",记录窗口,此时即为最小覆盖子串对应的结果窗口。


传送门:最小覆盖子串

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

If there is no such window in S that covers all characters in T, return the empty string “”.

If there is such window, you are guaranteed that there will always be only one unique minimum window in S.

给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。

如果 S 中不存这样的子串,则返回空字符串 “”。

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

示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"


/**
 *
 * Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
 * 给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。
 *
 */

public class MinimumWindowSubstring {
    public static void main(String[] args){
        System.out.println(minWindow("ADOBECODEBANC", "ABC"));
    }

    public static String minWindow(String s, String t) {

        //将模板存在元素及其个数装入桶template中
        int[] template = new int[58];
        for(int i = 0; i < t.length(); i++){
            ++template[t.charAt(i) - 'A'];
        }

        int begin = 0;
        int end = 0;
        int[] match = new int[58];
        int num = 0;
        int resultBegin = 0;
        int resultEnd = Integer.MAX_VALUE;

        while(end < s.length()){
            //若当前元素i不为模板元素,或当前元素出现次数已经超过模板次数,记录match,窗口右侧向前滑动
            int loc = s.charAt(end) - 'A';
            ++end;
            if(++match[loc] <= template[loc]){
                //对元素总数进行计数,检查个数是否与模板个数相等
                if(++num == t.length()){
                    //若元素总数满足,将滑动窗口左侧前移(跳过所有非模板元素和过多的模板元素),直到窗口为最小满足窗口
                    int leftloc = s.charAt(begin) - 'A';
                    while(begin < end && template[leftloc] < match[leftloc]){
                        --match[leftloc];
                        leftloc = s.charAt(++begin) - 'A';
                    }
                    //更新最小窗口,,更新完后窗口左侧前移一位,继续进行窗口滑动
                    if(end - begin < resultEnd - resultBegin){
                        resultBegin = begin;
                        resultEnd = end;
                    }
                    ++begin;
                    --match[leftloc];
                    --num;
                }
            }
        }
        return resultEnd == Integer.MAX_VALUE ? "" : s.substring(resultBegin, resultEnd);
    }

}



#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值