3.无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
解法二:
labuladong 相关滑动窗口题
567.字符串的排列
class Solution{
public:
//labuladong
// 判断 s 中是否存在 t 的排列
bool checkInclusion(string t, string s) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
char c = s[right];
right++;
// 进行窗口内数据的一系列更新
if (need.count(c)) {
//两个地方更新加一
window[c]++;
if (window[c] == need[c])
valid++;
}
// 判断左侧窗口是否要收缩
while (right - left >= t.size()) {
// 在这里判断是否找到了合法的子串
if (valid == need.size())
return true;
char d = s[left];
left++;
// 进行窗口内数据的一系列更新
if (need.count(d)) {
//两个地方更新减一
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
// 未找到符合条件的子串
return false;
}
209.长度最小的子数组
解法一:滑动窗口
解法二:前缀和+二分
class Solution{
public:
int minSubArrayLen(int s,vector<int>& nums){
if(nums.size() == 0) return 0;
int n = nums.size();
int ans = INT_MAX;
vector<int> preNums(n+1);
for(int i = 0;i<n;i++){
preNums[i+1] = preNums[i] + nums[i];
}
for(int i = 0;i<=n;i++){
int l = -1,r = n+1;
int t = s + preNums[i];//s为目标值,t为动态目标值
while(l + 1 < r){
int mid = (l+r)>>1;
if(preNums[mid] >= t){ //在红区找,返回满足时的r,preNums[r] - preNums[i] >= s(目标值) 前缀和的两值之差就是 区间和本身 区间数量r-i
r = mid;
}else{
l = mid;
}
}
if(r != n+1){
ans = min(ans,r-i);//i在preNums数组移动,符合子数组长度 更小最小值
}
}
return ans == INT_MAX ? 0:ans;
}
};
76.最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
class Solution{
public:
string minWindow(string s,string t){
unordered_map<char,int> need,window;
int valid = 0;//符合条件的t字符个数
int left = 0,right = 0;
//最小覆盖字串的起始位置,及长度
//数组哈希
int start = 0,len = INT_MAX;
for(char c : t){
need[c]++;
}
while(right < s.size()){//右指针右移,找到符合条件的窗口
//c 为添加窗口的字符
char c = s[right];
right++;
if(need.count(c)){
window[c]++;
if(need[c] == window[c]){
valid++;
}
}
//找到符合符合条件(valid)的窗口,左指针右移,优化窗口
while(valid == need.size()){
if(right - left < len){
len = right - left;
start = left;
}
//d 为移除窗口的字符
char d = s[left];
left++;
if(need.count(d)){
if(need[d] == window[d]){
valid--;
}
window[d]--;
}
}
}
return len == INT_MAX ? "" : s.substr(start,len);//开始位置,取个数
}
};
30.串联所有单词的子串
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> res;
if(words.size()==0) return res;
int word_size = words[0].size();
int word_num = words.size();
//字符串数组哈希记录
unordered_map<string,int> m1;
for(int i = 0;i < word_num;i++){
m1[words[i]]++;
}
//s字符串哈希记录
unordered_map<string,int> m2;
//i+word_size*word_num <= s.size() 0+6 <= 6 满足
for(int i = 0;i+word_size*word_num <= s.size();i++){
int j = 0;
//j=j+word_size 更新位置
for( j = i;j<i+word_size*word_num;j=j+word_size){
string tmp_str = s.substr(j,word_size);//从下标j开始取word_size大小 的子串
if(m1[tmp_str] == 0){
break;
}else{
m2[tmp_str]++;
//m2统计的字符串大于m1 跳出内for循环
if(m2[tmp_str]>m1[tmp_str]) break;
}
}
//j到i开始的一个连接完整子串位置
//每一段都符合,则加入答案
if(j==(i+word_size*word_num)){
res.push_back(i);
}
m2.clear();
}
return res;
}
};
25. K 个一组翻转链表
输入 head,reverseKGroup 函数能够把以 head 为头的这条链表进行翻转。
我们要充分利用这个递归函数的定义,把原问题分解成规模更小的子问题进行求解。
1、先反转以 head 开头的 k 个元素。 [a,b) reverse(a,b)
2、将第 k + 1 个元素作为 head 递归调用 reverseKGroup 函数。
将上述两个过程的结果连接起来。 a->next = reverseKGroup(b,k)
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
if(head == nullptr) return head;
ListNode* a = head;
ListNode* b = head;
for(int i = 0;i < k;i++){
if(b == nullptr) return a; // 小于等于k的长度不转化 return 原来的头节点
b = b->next;
}
ListNode* newHead = reverse(a,b); // 反转区间[a,b)
a->next = reverseKGroup(b,k); // 原a->next 下一个节点为递归的新节点
return newHead;
}
//反转 区间的节点
ListNode* reverse(ListNode* a,ListNode* b){
//三个节点,前当后
ListNode* pre = nullptr;
ListNode* cur = a;
ListNode* nt = a;
while(cur != b){
nt = cur->next;
cur->next = pre;
pre = cur;
cur = nt;
}
return pre;//反转后的新头节点
}
};