CodeStop算法题

本文介绍了几种基于滑动窗口的字符串处理算法,包括查找无重复字符的最长子串、判断字符串是否存在目标子串的排列、求解长度最小的子数组以及最小覆盖子串的问题。此外,还涉及到了K个一组翻转链表的递归解法。这些方法主要利用了滑动窗口、前缀和以及二分查找等技术来优化解决问题。
摘要由CSDN通过智能技术生成
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 个一组翻转链表

输入 headreverseKGroup 函数能够把以 head 为头的这条链表进行翻转。

我们要充分利用这个递归函数的定义,把原问题分解成规模更小的子问题进行求解。

1、先反转以 head 开头的 k 个元素。 [a,b) reverse(a,b)

2、将第 k + 1 个元素作为 head 递归调用 reverseKGroup 函数。

  1. 将上述两个过程的结果连接起来。 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;//反转后的新头节点
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值