题目地址:
https://leetcode.com/problems/minimum-window-substring/
给定两个字符串 s s s和 t t t,寻找 s s s中的最短的子串,使其包含 t t t中所有字符(重复次数也要算)。返回那个最短子串。如果无解,则返回空串。
先开个哈希表,存一下 t t t中每个字符出现了多少次(由于字符的范围是有限的,所以这里的哈希表也可以用长 128 128 128的数组代替),然后开一个变量 c c c存一下 t t t里有多少种不同字符。
接下来用双指针算法。开两个指针
i
,
j
i,j
i,j,一开始有
i
=
j
=
0
i=j=0
i=j=0。令哈希表
h
h
h维护的是
t
−
s
[
j
:
i
]
t-s[j:i]
t−s[j:i](即
t
t
t所含的字母及出现次数减去
s
[
j
:
i
]
s[j:i]
s[j:i]所含字母及出现次数形成的哈希表),令
c
c
c维护的是
t
t
t有多少种字符在
s
[
j
:
i
]
s[j:i]
s[j:i]中没有出现。对于每个
i
i
i,我们求最右边的使得
t
⊂
s
[
j
:
i
]
t\subset s[j:i]
t⊂s[j:i]的
j
j
j,对于
i
1
<
i
2
i_1<i_2
i1<i2,显然
j
j
j有单调性,即
j
1
≤
j
2
j_1\le j_2
j1≤j2。循环开始时首先h[s[i]]--
,此为维护
h
h
h的定义。如果h[s[i]] == 0
则说明
t
t
t中某个字母被完全凑出来了,则c--
。如果c == 0
,说明
t
t
t的每一个字母都被凑出来了,此时的
s
[
j
:
i
]
s[j:i]
s[j:i]满足
t
⊂
s
[
j
:
i
]
t\subset s[j:i]
t⊂s[j:i]。而我们要找到最右边满足条件的
j
j
j,于是我们看h[s[j]]
,如果h[s[j]] < 0
,说明即使去掉
s
[
j
]
s[j]
s[j],即当前的
s
[
j
+
1
:
i
]
s[j+1:i]
s[j+1:i]也能完全包含
t
t
t,从而我们不停让
j
j
j右移。当
j
j
j停下的时候,说明h[s[j]]
恰好等于
0
0
0了,那说明如果让
j
j
j再右移一步,即
t
t
t中将有一个字母无法被
s
[
j
+
1
:
i
]
s[j+1:i]
s[j+1:i]覆盖,此时的
j
j
j即为最右的满足条件的
j
j
j,从而我们可以更新答案。遍历所有的
i
i
i即知道全局最优解。
代码如下:
class Solution {
public:
string minWindow(string s, string t) {
int cnt[128];
fill(cnt, cnt + 128, 0);
int c = 0;
for (char ch : t)
if (++cnt[ch] == 1) c++;
int l = -1, r;
for (int i = 0, j = 0; i < s.size(); i++) {
if (!--cnt[s[i]]) c--;
if (!c) {
while (cnt[s[j]] < 0) cnt[s[j++]]++;
if (l == -1 || i - j < r - l) l = j, r = i;
}
}
return ~l ? s.substr(l, r - l + 1) : "";
}
};
时间复杂度 O ( l s + l t ) O(l_s+l_t) O(ls+lt),空间 O ( 1 ) O(1) O(1)。