给定一个字符串,找出字符串中包含{’a’,’b’,’c’}字符集的最短子串

给定字符串str,字符集{’a’,’b’,’c’},’a’,’b’,’c’的顺序可以改变,要找出包含字符集中所有字符的最短字串。

  • Record记录每个字符是否出现和最后出现的位置,minLength记录最短子串长度。
  • 设置front, rear指针。rear遍历str字符串,当遇到字符集中的字符时更新该字符的标记和位置。
  • 在front到rear区间,判断字符集中的字符是否都出现了,若是,从所有字符最后出现的位置中找出最小和最大的位置min、max,则区间内最短子串长度为max - min + 1。
  • front指针用于优化。当字符集中的字符都出现后,front指针置为min + 1, 并且清除min位置的字符状态。
  • 时间复杂度为O(n)。

参考了http://blog.csdn.net/expleeve/article/details/32715791 这个博客,但是他的front指针每次只移动1位,如果这一位本来就不包含在最短的子串里,那么没有什么实际意义,所以这里改进了一下,每次都移到min + 1的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#include<string>
using namespace std;

struct Record{
bool isAppear;
int lastPos;
};

map<char, Record> mp;
int front, rear;
int minLength;

string str = "ababaaassaxxxaaacccd";
char charset[3] = {'a', 'b', 'c'};

bool (){
for(auto it = mp.begin();it != mp.end(); it++){
if(!it->second.isAppear){
return false;
}
}
return true;
}

void updateFront(int pos){
front = pos + 1;
mp[str[pos]].isAppear = false;
}

int countLength(){
int max = -1, min = INT_MAX;
for(auto it = mp.begin(); it != mp.end(); it++){
if(it->second.lastPos > max)
max = it->second.lastPos;
if(it->second.lastPos < min)
min = it->second.lastPos;
}
updateFront(min);
return max - min + 1;
}

int main(){

for(int i = 0; i < 3; i++){
Record r;
r.isAppear = false;
mp[charset[i]] = r;
}
front = 0, rear = 0;
minLength = INT_MAX;

for(; rear < str.length(); rear++){
if(mp.find(str[rear]) != mp.end()){
mp[str[rear]].isAppear = true;
mp[str[rear]].lastPos = rear;
if(isValid()){
int len = countLength();
minLength = len < minLength ? len : minLength;
}
}
}
printf("minLength = %dn", minLength);
return 0;
}