面试题 17.18. 最短超串(滑动窗口)

1. 问题描述:

假设你有两个数组,一个长一个短,短的元素均不相同。找到长数组中包含短数组所有的元素的最短子数组,其出现顺序无关紧要。

返回最短子数组的左端点和右端点,如有多个满足条件的子数组,返回左端点最小的一个。若不存在,返回空数组。

示例 1:

输入:
big = [7,5,9,0,2,1,3,5,7,9,1,1,5,8,8,9,7]
small = [1,5,9]
输出: [7,10]
示例 2:

输入:
big = [1,2,3]
small = [4]
输出: []
提示:

big.length <= 100000
1 <= small.length <= 100000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-supersequence-lcci

2. 思路分析:

① 题目不难理解,由题目可以知道,我们求解的是big数组中的一个小范围,并且这个小范围中包含着small数组中的所有元素而且这个小范围的长度是最短的,其实仔细分析这些特点可以知道这个是一道经典的滑动窗口的题目,与之前的领扣中的76题几乎是一样的只是两道题目的场景不一样但是本质上是一样的

② 其实知道使用滑动窗口的策略来解决,剩下来的就比较简单了,对于这道题目来说使用滑动窗口来解决主要有以下几个步骤:

(1)因为是求解的小范围并且需要包含small数组的全部元素,所以我们需要使用map来记录small数组中各个元素出现的次数这样在接下来的窗口的缩减会比较方便

(2)使用List<Pair<Integer, Integer>>数据结构来记录big数组中所有与small数组相等的元素,这样可以记录一下这些元素出现的位置

(3)声明l、r变量来记录左右窗口的位置,我们首先需要扩展r右窗口,使其l~r能够覆盖small中的所有元素,使用一个额外的map来记录List中big数组中元素在滑动窗口的时候出现的次数,并且使用一个变量来统计滑动窗口的时候覆盖了small数组的多少个元素,假如等于了small数组元素的时候说明是找到了一个能够覆盖small数组的所有元素的范围,并且这个范围是当前最小的范围(使用List来记录这些数字的好处),这个时候判断一下是否与历史上得到的结果更短,假如更短则更新,并且我们需要缩小一下窗口范围,因为有的数字可能是重复的,对长度没有什么贡献作用,删除掉之后还是能够覆盖small数组的所有元素,所以会变得更短,所以使用map来统计这些数字出现的次数就使得这些判断变得很方便,当发现有影响的时候找到那么需要扩展右窗口找下一个满足条件的窗口,注意最后可能找不到符合条件的范围所以需要返回空数组,其中返回空数组为return new int[0];

③ 几乎与之前76题的思路是一模一样的,我们以后看到这样的题目就可以知道使用滑动窗口来解决了

3. 代码如下:

class Solution {
     public int[] shortestSeq(int[] big, int[] small) {
        /*统计small数组中数字出现的次数*/
        if (big.length < small.length) return new int[0];
        Map<Integer, Integer> smallTimes = new HashMap<>();
        for (int i = 0; i < small.length; ++i){
            smallTimes.put(small[i], smallTimes.getOrDefault(small[i], 0) + 1);
        }
        /*使用map来映射数字出现的位置*/
        List<Pair<Integer, Integer>> numPos = new ArrayList<>();
        for (int i = 0; i < big.length; ++i){
            if (smallTimes.containsKey(big[i])){
                numPos.add(new Pair<>(big[i], i));
            }
        }
        int l = 0, r = 0, cur = 0;
        /*使用一个map来统计big数组字符出现的次数*/
        Map<Integer, Integer> numsCount = new HashMap<>();
        int len = -1;
        int ans[] = {0, 0};
        while (r < numPos.size()){
            int curkey = numPos.get(r).getKey();
            int curNumsCount = numsCount.getOrDefault(curkey, 0);
            if (curNumsCount < smallTimes.get(curkey)) ++cur;
            numsCount.put(curkey, curNumsCount + 1);
            while (l <= r && cur == small.length){
                /*缩小范围*/
                int start = numPos.get(l).getValue();
                int end = numPos.get(r).getValue();
                if (len == -1 || end - start + 1 < len){
                    len = end - start + 1;
                    ans[0] = start;
                    ans[1] = end;
                }
                int leftkey = numPos.get(l).getKey();
                ++l;
                numsCount.put(leftkey, numsCount.get(leftkey) - 1);
                /*删除的字母有影响*/
                if (numsCount.get(leftkey) < smallTimes.get(leftkey)){
                    --cur;
                    break;
                }
            }
            ++r;
        }
        return len == -1 ? new int[0] : ans;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值