思路:
本题的是要求最短字串,由于这种特殊性,我们势必要考虑遍历所有元素来求得最短子串,那么一般这个时候会使用滑动窗口来优化。本题的滑动窗口的思路和普通的窗口是不同的,但是本质上都是符合一定条件的情况下移动右指针,或者左指针。本题的步骤如下:
(1)起始两个指针都指向第一个元素,移动右指针,直到子串的所有字符都出现。
(2)移动右指针剔除无用元素和重复元素。
(3)寻找临界点,计算长度。
(1)因为之后要进行字符串的比较来判断子串元素是否出现,所以考虑使用哈希表对短串的字符进行储存。用字符当key,出现就把value+1。
int n = s.size(), m = t.size();
unordered_map<char, int>mp;
for (auto& ch : t)
mp[ch]++;
如何判断是否所有字符都出现呢,因为我们有短串的字符长度,所以我可以用长度作为变量来统计是否所有字符都出现,也就是说出现一次,长度-1,这样当长度为0的时候,表示所有字符都出现了。当然为了避免重复字符消耗长度次数,要结合哈希表来进行处理。如果,”A“这个字符第一次出现的时候,对应哈希表的值为1,当值大于0的时候表示该字符第一次出现,就可以把值变为0,同时长度-1。下次再遇到"A"的时候,值不为0也就不再减少次数了。当然都是在for循环中实现的。
if (mp.count(s[i])) {
if (mp[s[i]] > 0)
mp[s[i]]--, m--;
else
mp[s[i]]--;
(2)当所有字符都出现后,为了避免有重复字符,同时为了寻求到字段子串必然要剔除一些无用字符。我们需要分情况进行处理,但是总的来说都是对左指针进行右移来实现的。
启动条件是m==0也就是出现所有字符后。
首先我们要知道对应的哈希表里面只可能有2种情况,①值为0,代表这一个子串中,该元素只出现了一次②值不为0,代表该元素重复出现了。
所以我们依次对左指针的元素判断时就有以下几种情况①这个字符在子串没出现过,所以在哈希表中也没有这个字符,那么直接左指针移动就可以了。②如果对应这个值为0就代表如果我再移动,剩下的子串就不包含这个字符了,也就是不再是符合要求的子串了,这个时候就可以计算长度了。②其他情况,这里就对应该字符是重复字符,对应的哈希表的值必须++,然后再右移。
在计算长度的时候,同时记录长度和左起第一个字符的位置,这样最后返回的时候就能打印出中间的字符串了。
while (m == 0) { // 始终满足条件一:包含子串
if (!mp.count(s[st])) {
st++;
continue;
}
if (mp[s[st]] == 0) { // 临界点
if (i - st + 1 < len) { // 找到更小的就更新答案
ans = st;
len = i - st + 1;
}
break;
}
else { // 多余字符可以缩短长度,st++
mp[s[st]]++;
st++;
}
以下用来处理空字符串和返回结果。
if (len == 1e6) return "";
return s.substr(ans, len);