最短超串
难度:
标签:
题目描述
You are given two arrays, one shorter (with all distinct elements) and one longer. Find the shortest subarray in the longer array that contains all the elements in the shorter array. The items can appear in any order.
Return the indexes of the leftmost and the rightmost elements of the array. If there are more than one answer, return the one that has the smallest left index. If there is no answer, return an empty array.
Example 1:
Input:
big = [7,5,9,0,2,1,3,5,7,9,1,1,5,8,8,9,7]
small = [1,5,9]
Output: [7,10]
Example 2:
Input:
big = [1,2,3]
small = [4]
Output: []
Note:
big.length <= 100000
1 <= small.length <= 100000
代码结果
运行时间: 73 ms, 内存: 30.2 MB
/*
* Problem: Given two arrays, one large and one small, where the elements of the small array are all unique.
* Find the shortest subarray in the large array that contains all elements of the small array, regardless of their order.
* Return the left and right indices of the shortest subarray. If there are multiple such subarrays, return the one with the smallest left index.
* If no such subarray exists, return an empty array.
*
* Example 1:
* Input:
* big = [7,5,9,0,2,1,3,5,7,9,1,1,5,8,8,9,7]
* small = [1,5,9]
* Output: [7,10]
*
* Example 2:
* Input:
* big = [1,2,3]
* small = [4]
* Output: []
*/
import java.util.*;
import java.util.stream.Collectors;
public class SolutionStream {
public int[] shortestSeq(int[] big, int[] small) {
Set<Integer> smallSet = Arrays.stream(small).boxed().collect(Collectors.toSet());
Map<Integer, Long> countMap = Arrays.stream(small).boxed().collect(Collectors.groupingBy(e -> e, Collectors.counting()));
Map<Integer, Long> windowMap = new HashMap<>();
int left = 0, minLeft = 0, minRight = Integer.MAX_VALUE;
for (int right = 0; right < big.length; right++) {
if (smallSet.contains(big[right])) {
windowMap.put(big[right], windowMap.getOrDefault(big[right], 0L) + 1);
}
while (windowMap.size() == countMap.size() && windowMap.entrySet().containsAll(countMap.entrySet())) {
if (right - left < minRight - minLeft) {
minLeft = left;
minRight = right;
}
if (smallSet.contains(big[left])) {
windowMap.put(big[left], windowMap.get(big[left]) - 1);
if (windowMap.get(big[left]) == 0) windowMap.remove(big[left]);
}
left++;
}
}
return minRight == Integer.MAX_VALUE ? new int[]{} : new int[]{minLeft, minRight};
}
}
解释
方法:
本题解采用了滑动窗口的策略来找到包含所有'small'数组元素的最短子数组。首先,使用字典'cnt'来记录'small'中每个元素的计数,此时每个元素的计数初始化为1。接着,定义两个指针'left'和'right'表示当前窗口的左右端点,并使用变量'need'来表示还需包含的'small'数组的元素数。通过移动'right'指针扩展窗口,并在每次移动时更新'cnt'和'need'。当'need'为0时,说明窗口已包含所有'small'的元素,此时尝试通过移动'left'指针缩小窗口以寻找更短的子数组。每次更新窗口时都检查并更新最小长度的子数组。如果在整个过程中找到了符合条件的子数组,则记录其起始和结束位置。
时间复杂度:
O(n)
空间复杂度:
O(m)
代码细节讲解
🦆
在初始化`cnt`字典时,为什么选择将`small`数组中的每个元素计数初始化为1?
▷🦆
如果在`big`数组中某个元素的计数已经是负数,为什么仍然减少`need`值?
▷🦆
在缩小窗口的过程中,为什么要检查`cnt[big[left]] >= 0`才增加`need`,而不是直接增加?
▷