1、题目描述
给定一个序列如S = {1,8,2,1,4,1,2,9,1,8,4},我们需要找到包含S的所有元素的最小长度子序列(不重复,顺序不重要)。如何有效地找到这个子序列?注意:S:{1,2,4,8,9 }中有5个不同的元素,最小长度子序列必须包含所有这5个元素。
2、解题思想
(1)暴力求解(时间O(n^2),空间0)
找出序列中的所有子串,判断每个子串是否包含所有元素。时间复杂度为O(n^2),这种做法面试通不过。
(2)使用哈希表(时间O(n),空间O(m),其中m为不同元素个数)
首先遍历序列,找出序列中的有元素,用一个map来保存每个元素在序列中已经找到的个数,初时刻,这个map的所有元素的初始值全部为0。使用两个指针begin和end分表保存序列的窗口,使用一个count变量保存已经找到的序列中不同元素的个数。
从头开始遍历序列,遇到一个元素elem时,如果map[elem]的值为0,则map[elem]加1且count加1。如果map[elem]的值大于0,则map[elem]加1,count不变。当count的值等于序列中不相同的元素个数时,表示找到一个满足需求的子窗口,此时保存这个窗口的大小和起始位置。接着begin指针往后移动,直到map中有一个元素的map[elem]为0,然后count--,寻找下一个满足需求的窗口。代码实现如下:
#include<iostream>
#include<vector>
#include<map>
using namespace std;
/*
找出包含序列的所有元素的最小长度子序列
*/
void MinLengthSeq(vector<int> input) {
int begin = 0; //保存窗口的起始位置
int count = 0; //保存已找到的不同元素的个数
int windos = input.size(); //保存最小的窗口大小
int start = 0; //保存所找到的最小窗口的位置
map<int, int> has_found_elem; //统计找到的每个元素的个数
for (int i = 0; i < input.size(); i++)
has_found_elem[input[i]] = 0;
for (int i = 0; i < input.size(); i++) {
if (has_found_elem[input[i]] == 0) { //如果找到一个不同的元素,count++,map中元素个数加一
has_found_elem[input[i]]++;
count++;
}
else //如果找到相同的元素,count不变,map中元素个数加一
has_found_elem[input[i]]++;
if (count == has_found_elem.size()) { //如果count大小等于不相同的元素个数,则找到一个窗口
if (windos > i - begin + 1) {
windos = i - begin + 1;
start = begin;
}
while (has_found_elem[input[begin]] > 0) { //begin指针往后移动机,直到map中有一个元素个数为0
has_found_elem[input[begin]]--;
if (has_found_elem[input[begin++]] == 0)
break;
}
count--; //count--,去寻找下一个满足要求的窗口
}
}
cout << "length:" << windos << endl;
cout << "start:" << start << endl;
}
参考:https://blog.csdn.net/yangwenxue_admin/article/details/44568069