前言
一个本硕双非的小菜鸡,备战24年秋招,计划刷完hot100和剑指Offer的刷题计划,加油!
根据要求,每一道题都要写出两种以上的解题技巧。
一、76. 最小覆盖子串(HOT100)
76. 最小覆盖子串
Note:滑动窗口
过程如下:
1、遍历t字符串,用ht哈希表记录t字符串各个字符出现的次数。
2、定义两个指针j和i,j指针用于收缩窗口,i指针用于延伸窗口,则区间[j,i]表示当前滑动窗口。首先让i和j指针都指向字符串s开头,然后枚举整个字符串s ,枚举过程中,不断增加i使滑动窗口增大,相当于向右扩展滑动窗口。
3、每次向右扩展滑动窗口一步,将s[i]加入滑动窗口中,而新加入了s[i],相当于滑动窗口维护的字符数加一,即hs[s[i]]++。
4、对于新加入的字符s[i],如果hs[s[i]] <= ht[s[i]],说明当前新加入的字符s[i]是必需的,且还未到达字符串t所要求的数量。我们还需要事先定义一个cnt变量, cnt维护的是s字符串[j,i]区间中满足t字符串的元素的个数,记录相对应字符的总数。新加入的字符s[i]必需,则cnt++。
5、我们向右扩展滑动窗口的同时也不能忘记收缩滑动窗口。因此当hs[s[j]] > ht[s[j]时,说明hs哈希表中s[j]的数量多于ht哈希表中s[j]的数量,此时我们就需要向右收缩滑动窗口,j++并使hs[s[j]]–,即hs[s[j ++ ]] --。
6、当cnt == t.size时,说明此时滑动窗口包含符串 t 的全部字符。我们重复上述过程找到最小窗口即为答案。
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> hs, ht;
for (auto c : t) ht[c]++;
string res;
int nums = 0;
for (int i = 0, j = 0; i < s.size(); i++) {
hs[s[i]]++;
if (hs[s[i]] <= ht[s[i]]) nums++;
while (hs[s[j]] > ht[s[j]]) hs[s[j++]]--;
if (nums == t.size()) {
if (res.empty() || i - j + 1 < res.size())
res = s.substr(j, i - j + 1);
}
}
return res;
}
};
Note:
- 使用滑动窗口,i为左指针,j为右指针。、
- 用了两个数组,一个数组need[128]表示所需字符及个数,一个数组have[128]动态维护窗口中的字符及的个数。
- 额外使用一个变量need_num,记录当前所需字符数量,当数量为0即满足需求。
思路:
- j指针不断右移,并且将当前字符填充到数组have,如果当前字符为所需字符,且have数量<need,则need_num–
- 当need_num=0时,此时j为可行窗右边界,因此,进行下一步,将i移动到左边界。
- i指针不停右移,并且have数量-1,当have[s[i]] < need[s[i]]时说明这个数的去除使得需求不满足,则i为左边界。 此时便可以计算子字符串长度了
class Solution {
public:
string minWindow(string s, string t) {
//定义两个数组,记录所需字符,和已有字符
int need[128] ={0}; //ASCII表总长128
int have[128] ={0};
int need_num=0;//计算还需要多少个字符
for (char i:t) //记录所需字符数量
{
need[i]++;
need_num++;
}
//ij双指针,子字符串长度,子字符串开始位置
int i = 0, j = 0, subString_len = INT_MAX, subString_start = 0;
for(j=0; j < s.size();j++)//j指针遍历
{
have[s[j]]++;//填充
if(need[s[j]] && (have[s[j]]<=need[s[j]])) //如果在需求里,并且没有超过需求的情况下
need_num--;
if(need_num==0)//满足所有需求,则此时j指针指向窗口右侧
{
for(;i<=j;i++)//i 不停右移,寻找左边界
{
have[s[i]]--;
//如果去掉这个数使得已有的数比需要的数少,则说明此时i为左边界
if(need[s[i]] && (have[s[i]] < need[s[i]]))
{
int len=j-i+1;
if(len < subString_len)//如果现有长度小于默认长度
{
subString_start=i;//记录起点
subString_len=len;//记录长度
}
i++;//i前进一格
need_num++;//需求数量+1
break;
}
}
}
}
if(subString_len==INT_MAX)
return "";
return s.substr(subString_start,subString_len);
}
};
二、21. 调整数组顺序使奇数位于偶数前面(剑指Offer)
Note:简单思路。设置两个头尾指针维护。往中间扫描。扫描时保证第一个指针前面的数都是奇数,第二个指针后面的数都是偶数。
class Solution {
public:
void reOrderArray(vector<int> &array) {
int size = array.size();
if (size <= 1) return;
int leftIndex = 0;
int rightIndex = size - 1;
while (leftIndex != rightIndex) {
if ((array[leftIndex] % 2 == 0) && (array[rightIndex] % 2 == 1))
swap(array[leftIndex], array[rightIndex]);
else if (array[leftIndex] % 2 == 1)
leftIndex++;
else
rightIndex--;
}
return;
}
};
Note:申请一个新空间,然后遍历两遍挨个往里塞。(offer消失术)
class Solution {
public:
void reOrderArray(vector<int>& array) {
vector<int> a;
for (int x : array) {
if (x % 2)
a.push_back(x);
}
for (int x : array) {
if (x % 2 == 0)
a.push_back(x);
}
array = a;
}
};
总结
祝大家都能学有所成,找到一份好工作!