12 哈希+不定长滑窗:最小覆盖子串 76
最小覆盖子串
又是一道硬题。
咱今天先把题干给读懂。
不定长滑窗
ADOBEC
DOBECODEBA
OBECDEBA
BECDEBA
ECDEBA
CDEBA
DEBANC
EBANC
BANC
ANC
我的思路是。
双指针,右指针不断向右滑动。
Q1.左指针什么时候动?
当右指针发现子串已经覆盖ABC(具体如何实现呢)时,左指针向右滑动,直到不能覆盖子串。
Q2.说得简单,如何知道子串有没有覆盖ABC呢?
可以把数组当做哈希表,26个字母对应26个下标,碰到了哪个字符就让哪个字符+1。
难道说右指针每次移动都要遍历一遍这个哈希表,看看是否满足条件吗?
这样时间复杂度是O(n^2)啊。
脑子崩了。
今天已经是6月1日了,又试着写了写,写不出来。
上题解!
点击查看代码
class Solution {
bool is_covered(int cnt_s[], int cnt_t[]) {
for (int i = 'A'; i <= 'Z'; ++i) {
if (cnt_s[i] < cnt_t[i])
return false;
}
for (int i = 'a'; i <= 'z'; ++i) {
if (cnt_s[i] < cnt_t[i])
return false;
}
return true;
}
public:
string minWindow(string s, string t) {
int cnt_s[128]{};
int cnt_t[128]{};
bool flag = false;
int len = INT_MAX, start = 0;
int left = 0;
for (auto& ch : t) {
++cnt_t[ch];
}
for (int i = 0; i < s.length(); ++i) {
// 元素进入窗口
++cnt_s[s[i]];
if (i < t.length() - 1)
continue;
while (is_covered(cnt_s, cnt_t)) {
if (len > i - left + 1) {
len = i - left + 1;
start = left;
}
--cnt_s[s[left]];
++left;
}
}
if (len == INT_MAX)
return "";
return s.substr(start, len);
}
};
碎碎念。
总算通过了。
我刚开始的思路是正确的。
就是两个哈希表分别记录这两个字符串的元素,然后对于主串就是滑动窗口,计数。
然后我没写出来主要是因为两点。
- 我最开始用的是数组array,是因为之前做过一道题array可以直接用==比较,但是又看题发现主串元素只要覆盖子串即可,所以>=也是可以的,那我就在想,这我该如何实现呢?
天哪,我都没想过自己写一个函数,比较a-z,A-Z就可以了,就是这样简单。 - 还有一点我在用数组的时候,我没想过是直接用char,我最开始用的是char-'a'。脑子瓦特了,一时间不知道A和a的ASCII码值谁大。
这下记住了A是小的。
可问题又出现了,char-A还能出现负数!
那我想的只有一种结果,那就是while循环因为并没有判断它和i的关系,所以left指向\0了……
所以最后我就直接用char了。
OK,题解明天再仔细看吧,要去吃饭了,再见!
2025-06-02 08:38:16 星期一
崭新的一天!
再来回顾一下这个最小覆盖子串的解法。
哈希表+不定长滑窗。
一个哈希表用来记录子串的字符,另一个哈希表记录遍历中的主串。
元素进入窗口,又指针右移;直到哈希表中的字符数大于等于子串哈希表字符数,开始检查主串能否覆盖子串。
Q1:覆盖如何判断?
。
若覆盖,则记录子串长度和子串的起点位置;
否则,剔除左指针所指元素,左指针右移。
时间复杂度:子串遍历O(m)+主串左指针和右指针至多移动O(n),每次循环还要比对两个哈希表是否相等O(26+26)
好像是O(m+52n)
空间复杂度:主要是两个哈希表,但也是O(1),因为数组长度不因那个字符串长度增长。
现在看题解,我们的思路和题解第一种方法是差不多的。
下面就是如何继续优化?
天哪,没心情看了,标记一下吧。