题目
假设你有两个数组,一个长一个短,短的元素均不相同。找到长数组中包含短数组所有的元素的最短子数组,其出现顺序无关紧要。
返回最短子数组的左端点和右端点,如有多个满足条件的子数组,返回左端点最小的一个。若不存在,返回空数组。
示例 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
代码
完整代码
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
void dfs(int* minHeadToTail, int** appearIndex, int* appearIndexIndex, int smallIndex, int nowSmallestIndex, int nowBiggestIndex, int smallSize, int* res, int* big, int bigSize) {
if (smallIndex == smallSize) {
int nowHeadToTail = nowBiggestIndex - nowSmallestIndex + 1;
if (nowHeadToTail < (*minHeadToTail) && nowHeadToTail > 0) {
(*minHeadToTail) = nowHeadToTail;
res[0] = nowSmallestIndex;
res[1] = nowBiggestIndex;
}
return;
}
for (int i = 0; i < appearIndexIndex[smallIndex]; i++) {
int index = appearIndex[smallIndex][i];
int tmpnowSmallestIndex = nowSmallestIndex;
int tmpnowBiggestIndex = nowBiggestIndex;
if (nowSmallestIndex == bigSize) {
nowSmallestIndex = index;
}
if (nowBiggestIndex == -1) {
nowBiggestIndex = index;
}
if (index > nowBiggestIndex) {
nowBiggestIndex = index;
} else if (index < nowSmallestIndex) {
nowSmallestIndex = index;
}
dfs(minHeadToTail, appearIndex, appearIndexIndex, smallIndex + 1, nowSmallestIndex, nowBiggestIndex, smallSize, res, big, bigSize);
nowSmallestIndex = tmpnowSmallestIndex;
nowBiggestIndex = tmpnowBiggestIndex;
}
}
int* shortestSeq(int* big, int bigSize, int* small, int smallSize, int* returnSize) {
int** appearIndex = (int**)calloc(smallSize, sizeof(int*));
int* appearIndexIndex = (int*)calloc(smallSize, sizeof(int));
for (int i = 0; i < smallSize; i++) {
appearIndex[i] = (int*)calloc(bigSize, sizeof(int));
for (int j = 0; j < bigSize; j++) {
appearIndex[i][j] = -1;
}
}
for (int i = 0; i < bigSize; i++) {
for (int j = 0; j < smallSize; j++) {
if (big[i] == small[j]) {
appearIndex[j][appearIndexIndex[j]++] = i;
break;
}
}
}
int* res = (int*)calloc(2, sizeof(int));
int minHeadToTail = bigSize + 1;
dfs(&minHeadToTail, appearIndex, appearIndexIndex, 0, bigSize, -1, smallSize, res, big, bigSize);
(*returnSize) = 2;
for (int i = 0; i < smallSize; i++) {
free(appearIndex[i]);
}
free(appearIndex);
free(appearIndexIndex);
if (minHeadToTail == bigSize + 1) {
(*returnSize) = 0;
return res;
}
return res;
}
思路分析
这套代码使用深度优先搜索(DFS)方法来解决这个问题。它首先记录每个 small
数组元素在 big
数组中出现的位置,然后通过DFS遍历所有可能的组合,找出包含所有 small
数组元素的最短子数组。
拆解分析
dfs
函数
void dfs(int* minHeadToTail, int** appearIndex, int* appearIndexIndex, int smallIndex, int nowSmallestIndex, int nowBiggestIndex, int smallSize, int* res, int* big, int bigSize) {
if (smallIndex == smallSize) {
int nowHeadToTail = nowBiggestIndex - nowSmallestIndex + 1;
if (nowHeadToTail < (*minHeadToTail) && nowHeadToTail > 0) {
(*minHeadToTail) = nowHeadToTail;
res[0] = nowSmallestIndex;
res[1] = nowBiggestIndex;
}
return;
}
for (int i = 0; i < appearIndexIndex[smallIndex]; i++) {
int index = appearIndex[smallIndex][i];
int tmpnowSmallestIndex = nowSmallestIndex;
int tmpnowBiggestIndex = nowBiggestIndex;
if (nowSmallestIndex == bigSize) {
nowSmallestIndex = index;
}
if (nowBiggestIndex == -1) {
nowBiggestIndex = index;
}
if (index > nowBiggestIndex) {
nowBiggestIndex = index;
} else if (index < nowSmallestIndex) {
nowSmallestIndex = index;
}
dfs(minHeadToTail, appearIndex, appearIndexIndex, smallIndex + 1, nowSmallestIndex, nowBiggestIndex, smallSize, res, big, bigSize);
nowSmallestIndex = tmpnowSmallestIndex;
nowBiggestIndex = tmpnowBiggestIndex;
}
}
dfs
函数用于递归搜索所有可能的子数组。- 当所有
small
数组元素都找到后,计算当前子数组的长度并更新最短长度及其起始和结束位置。 - 递归遍历
appearIndex
中每个元素的位置,更新最小和最大索引。
shortestSeq
函数
int* shortestSeq(int* big, int bigSize, int* small, int smallSize, int* returnSize) {
int** appearIndex = (int**)calloc(smallSize, sizeof(int*));
int* appearIndexIndex = (int*)calloc(smallSize, sizeof(int));
for (int i = 0; i < smallSize; i++) {
appearIndex[i] = (int*)calloc(bigSize, sizeof(int));
for (int j = 0; j < bigSize; j++) {
appearIndex[i][j] = -1;
}
}
for (int i = 0; i < bigSize; i++) {
for (int j = 0; j < smallSize; j++) {
if (big[i] == small[j]) {
appearIndex[j][appearIndexIndex[j]++] = i;
break;
}
}
}
int* res = (int*)calloc(2, sizeof(int));
int minHeadToTail = bigSize + 1;
dfs(&minHeadToTail, appearIndex, appearIndexIndex, 0, bigSize, -1, smallSize, res, big, bigSize);
(*returnSize) = 2;
for (int i = 0; i < smallSize; i++) {
free(appearIndex[i]);
}
free(appearIndex);
free(appearIndexIndex);
if (minHeadToTail == bigSize + 1) {
(*returnSize) = 0;
return res;
}
return res;
}
shortestSeq
函数首先初始化并填充appearIndex
,记录small
数组中每个元素在big
数组中出现的位置。- 然后调用
dfs
函数来搜索包含所有small
数组元素的最短子数组。 - 最后释放分配的内存,并返回结果。
复杂度分析
- 时间复杂度:
O(n * m)
,其中n
是big
数组的长度,m
是small
数组的长度。最坏情况下需要遍历所有可能的子数组组合。 - 空间复杂度:
O(n * m)
,用于存储appearIndex
和递归调用栈的空间。
一题多解 滑动窗口
滑动窗口代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int count;
int required;
} ElementInfo;
int* shortestSeq(int* big, int bigSize, int* small, int smallSize, int* returnSize) {
int* result = (int*)malloc(2 * sizeof(int));
*returnSize = 0;
if (smallSize > bigSize) {
return result;
}
ElementInfo* smallMap = (ElementInfo*)calloc(100000, sizeof(ElementInfo));
int requiredCount = 0;
for (int i = 0; i < smallSize; i++) {
if (smallMap[small[i]].required == 0) {
requiredCount++;
}
smallMap[small[i]].required++;
}
int left = 0;
int minLength = bigSize + 1;
int currentCount = 0;
for (int right = 0; right < bigSize; right++) {
if (smallMap[big[right]].required > 0) {
smallMap[big[right]].count++;
if (smallMap[big[right]].count == smallMap[big[right]].required) {
currentCount++;
}
}
while (currentCount == requiredCount && left <= right) {
if (right - left + 1 < minLength) {
minLength = right - left + 1;
result[0] = left;
result[1] = right;
*returnSize = 2;
}
if (smallMap[big[left]].required > 0) {
if (smallMap[big[left]].count == smallMap[big[left]].required) {
currentCount--;
}
smallMap[big[left]].count--;
}
left++;
}
}
free(smallMap);
if (*returnSize == 0) {
result[0] = -1;
result[1] = -1;
}
return result;
}
思路分析
这套代码使用滑动窗口方法来解决问题。通过维护两个指针 left
和 right
,动态调整窗口大小以找到包含所有 small
数组元素的最短子数组。
拆解分析
-
初始化
smallMap
记录small
数组中每个元素的出现次数和当前计数。requiredCount
记录small
数组中不同元素的个数。
-
滑动窗口
- 右指针
right
从左到右遍历big
数组。 - 当窗口包含所有
small
数组元素时,移动左指针left
以缩小窗口并更新最短子数组的起始和结束位置。
- 右指针
-
返回结果
- 如果没有找到包含所有
small
数组元素的子数组,返回[-1, -1]
。
- 如果没有找到包含所有
复杂度分析
- 时间复杂度:
O(n + m)
,其中n
是big
数组的长度,m
是small
数组的长度。滑动窗口方法需要遍历big
数组一次,同时更新smallMap
数组。 - 空间复杂度:O(m),用于存储
smallMap
数组。