209. 长度最小的子数组
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 连续子数组
[
n
u
m
s
l
,
n
u
m
s
l
+
1
,
.
.
.
,
n
u
m
s
r
−
1
,
n
u
m
s
r
]
[nums_l, nums_{l+1}, ..., nums_{r-1}, nums_r]
[numsl,numsl+1,...,numsr−1,numsr],并返回其长度。如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3]
是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
方法:滑动窗口
思路:在每次while
循环开始时,保持
s
u
m
<
t
a
r
g
e
t
sum < target
sum<target ,
s
u
m
sum
sum 保持
i
i
i 到
j
−
1
j -1
j−1 的和,但其实此时
j
=
j
j=j
j=j(有点废话)。每当
s
u
m
+
n
u
m
s
[
j
]
≥
t
a
r
g
e
t
sum + nums[j] \geq target
sum+nums[j]≥target 时,就是满足条件了,就要统计子数组的长度,但同时也要让
s
u
m
sum
sum 和
i
,
j
i, j
i,j 再次不满足条件,也就是让
i
i
i 向后走到不满足为止,成为
s
u
m
<
t
a
r
g
e
t
sum < target
sum<target,为下一次while
循环做准备。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
if(n == 0) return 0;
int sum = 0;
int i = 0, j = 0;
int minlong = n + 1;
while(j < n){
sum += nums[j];
while(sum >= target){
minlong = min(minlong, j - i + 1);
sum -= nums[i++];
}
j ++;
}
return minlong == n + 1 ? 0 : minlong;
}
};
3. 无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc” ,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b” ,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke” ,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke"
是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
思路:和上题基本一致
class Solution {
private:
unordered_map<char, int> hash;
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
if(n == 0) return 0;
int i = 0, j = 1;
hash[s[0]] = 1;
int long_s = 0;
while(j < n){
if(hash.find(s[j]) != hash.end()){
long_s = max(long_s, j - i);
while(s[i] != s[j]){
hash.erase(s[i]);
i++;
}
i++;
}
hash[s[j]] = 1;
j++;
}
return long_s = max(long_s, j - i);;
}
};
30. 串联所有单词的子串
给定一个字符串 s
和一个字符串数组 words
。 words
中所有字符串 长度相同。
s
中的 串联子串 是指一个包含 words
中所有字符串以任意顺序排列连接起来的子串。
例如,如果 words = ["ab","cd","ef"]
, 那么 "abcdef"
, "abefcd"
,"cdabef"
, "cdefab"
,"efabcd"
, 和 "efcdab"
都是串联子串。 "acdbef"
不是串联子串,因为他不是任何 words
排列的连接。
返回所有串联子串在 s
中的开始索引。你可以以 任意顺序 返回答案。
示例 1:
输入:s = “barfoothefoobarman”, words = [“foo”,“bar”]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 “barfoo” 开始位置是 0。它是 words 中以 [“bar”,“foo”] 顺序排列的连接。
子串 “foobar” 开始位置是 9。它是 words 中以 [“foo”,“bar”] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。
示例 2:
输入:s = “wordgoodgoodgoodbestword”, words = [“word”,“good”,“best”,“word”]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。
示例 3:
输入:s = “barfoofoobarthefoobarman”, words = [“bar”,“foo”,“the”]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 “foobarthe” 开始位置是 6。它是 words 中以 [“foo”,“bar”,“the”] 顺序排列的连接。
子串 “barthefoo” 开始位置是 9。它是 words 中以 [“bar”,“the”,“foo”] 顺序排列的连接。
子串 “thefoobar” 开始位置是 12。它是 words 中以 [“the”,“foo”,“bar”] 顺序排列的连接。
提示:
1 <= s.length <= 104
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i]
和 s
由小写英文字母组成
思路:
每步迈一个单词(因为单词等长),所以就要考虑起点在哪。如果单词长度为4,我们一步迈4个字母,那么如果起点是0,则第4个、第8个等等都会被迈到。所以起点就要考虑剩下的1,2,3情况。
用哈希表存储单词出现了几次,并用哈希表中数值的剩余量表示之后的匹配还需要多少次单词的出现。
每次匹配一整个单词,匹配上,right++
,,直到不匹配了;匹配不上,left++
,然后再回去看看现在right
能匹配上了吗。
但是容易忽略一种情况,就是right后面的整个单词完全不在words列表中的情况。
class Solution {
private:
unordered_map<string, int> hash;
public:
vector<int> findSubstring(string s, vector<string>& words) {
int n = s.size(), m = words[0].size(), nums = words.size();
string cut = "";
vector<int> res;
for(int i = 0; i < m; i ++){
//创建hash
hash.clear();
init_hash(words);
//用hash按照单词步长
int left = i, right = i;
while(right + m <= n){
cut = s.substr(right, m);
while(hash.find(cut) != hash.end()){
//找到
hash_delete(cut);
right += m;
if (right + m <= n) {
cut = s.substr(right, m);
}else{
break;
}
}
if(hash.empty()) res.push_back(left);
if(left < right){
cut = s.substr(left, m);
hash_insert(cut);
left += m;
}else{
//在接下来一个单词完全不再words中时,会进入到这里
right += m;
left = right;
}
}
}
return res;
}
void init_hash(vector<string>& words){
int nums = words.size();
for(int j = 0; j < nums; j ++){
hash_insert(words[j]);
}
}
void hash_insert(string cut){
if(hash.find(cut) == hash.end()){
hash[cut] = 1;
}else{
hash[cut] ++;
}
}
void hash_delete(string cut){
if(hash[cut] == 1){
hash.erase(cut);
}else{
hash[cut] --;
}
}
};
76. 最小覆盖子串
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
对于 t
中重复字符,我们寻找的子字符串中该字符数量必须不少于 t
中该字符数量。
如果 s
中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。
示例 2:
输入:s = “a”, t = “a”
输出:“a”
解释:整个字符串 s 是最小覆盖子串。
示例 3:
输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
提示:
m == s.length
n == t.length
1 <= m, n <= 105
s
和 t
由英文字母组成
思路:需要两个主要的哈希表,need
用来统计目前的窗口中是否包含所有字符串t
的字母,管理right
走多远;have
用来统计属于字符串t
的字母,但是比其要求的数量多了多少个,比如t="ABC"
,当前窗口是"AABC"
,这时候就多了一个"A"
,这个用来决定left
怎么走,left
会跨过不属于t
的以及多出来的存放在have
中的字母。我用了一个哈希表flag
用来判断字符是不是存在于字符串t
里的。
class Solution {
private:
unordered_map<char, int> have;
unordered_map<char, int> need;
unordered_set<char> flag;
public:
string minWindow(string s, string t) {
int n = s.size(), m = t.size();
if(m > n) return "";
int minlong = n + 1;
int left = 0, right = 0;
int min_start = -1;
init_hash(t);
while(right < n && left <= right){
while(!need.empty() && right < n){ //循环让右指针往右走
if(flag.find(s[right]) != flag.end()){
if(need.find(s[right]) != need.end()){
delete_need(s[right]);
}else{
insert_have(s[right]);
}
}
right ++;
}
while(left < right && (flag.find(s[left]) == flag.end() || have.find(s[left]) != have.end())){ //跨越所有不属于的和多余的
if(have.find(s[left]) != have.end()){
delete_have(s[left]);
}
left ++;
}
if(need.empty() && minlong > right - left){ // 满足条件就记录下来
minlong = right - left;
min_start = left;
}
delete_have(s[left]);//去掉满足条件的最左的字母,以便往后寻找可能存在的更小的
insert_need(s[left]);
left ++;
}
return min_start < 0 ? "" : s.substr(min_start, minlong);
}
void init_hash(string t){
int m = t.size();
for(int i = 0; i < m; i++){
if(flag.find(t[i]) == flag.end()){
flag.insert(t[i]);
need[t[i]] = 1;
}else{
need[t[i]] ++;
}
}
}
void delete_need(char c){
if(need[c] > 1){
need[c] --;
}else{
need.erase(c);
}
}
void delete_have(char c){
if(have.find(c) != have.end()){
if(have[c] > 1){
have[c] --;
}else{
have.erase(c);
}
}
}
void insert_have(char c){
if(have.find(c) == have.end()){
have[c] = 1;
}else{
have[c] ++;
}
}
void insert_need(char c){
if(need.find(c) == need.end()){
need[c] = 1;
}else{
need[c] ++;
}
}
};