LeetCode特训 --- Week2 (主打滑动窗口 + 字符串匹配题目)

news2024/12/23 0:01:47

目录

滑动窗口原理

真懂了滑动窗口?

滑动 + 字符串细节

开干切题


滑动窗口原理

滑动窗口:维护一前一后两根指针, 或者说一左一右两个指针。更主要的是维护左右指针中的区间. 同时不断的向前滑动,直到整个序列滑动结束,前指针走到序列末尾,所有结果得到收集。

分清职责:

区间维护(题目要求):restriction (限制),限制窗口中的元素。不是什么元素都要维护在窗口中的。必须符合要求,不是所有的水都叫做百岁山。

前指针(探路兵):主打一个铁憨憨,一生的唯一使命就是扩大窗口, 同时维护一个计数器或者是题目要求序列的一个积累判断。 核心:前指针,积累(符合题干的)序列,扩大区间。

后指针(区间头):主打一个收拢区间,收集结果。后指针,按道理应该始终指向(满足题干要求)序列的头部,收集结果。

怎么让后指针(区间的头)走到(符合要求)序列头部?是我们需要考虑的问题。也是一个随意写滑动窗口Bug频出的问题?

如果,兄弟们可以看懂上述理论,那接下来完全有必要可以再瞅瞅,如果看不懂理论,下面还有滑动窗口题目的诸多细节也可以学习。究其根本,滑动窗口也仅仅只是一种思维而已,具体如何实现,各有所好,上述也仅仅只是提供一种思维角度。题目做得慢,bug频出,很多时候是由于各位兄弟蒙没有去自己总结这样的套路。虽然,编程讲究随意性,大佬不管怎么写都没问题,但是人家哪些杀出来的,真不是咱这点小训练可比的,最好就是一周周,勤劳的一个一个小点的攻克,攻克完了总结出自己的切题模板,套路出来

真懂了滑动窗口?

提及滑动窗口, 这仿佛是刷题人必经之路, 人尽皆知的算法了。上面的懂了,是不是刷滑动窗口题目就很顺利了吗?那必然不是,鹅鹅鹅,为啥,上面的模板不是已经挺成型的吗?

NONONO. 滑动窗口题目除了核心整体考滑动窗口,他还可以结合字符串的知识点一起考核。

----- 而不同的字符串匹配规则,对应着不同的处理措施。

滑动 + 字符串细节

字符串基础: 
1. 子串 和 子序列的区别 
    子串: 连续子串              原串: abcdgf  目标串: abc  匹配子串:   abc   
    子序列: 子串中可以多出字符   原串: abcdgf  目标串: acd  匹配子序列: abcd  
2. 子串的排列 和 子序列的区别.   
    排列:包含所要求字符即可, 顺序可变    (应对措施,化变为不变,用位置映射消除顺序之别,map,set,或是vector均可.)    比如 原串:   acbdgh    目标串: [abc]  匹配子序列: acb. (包含acb字符即可)     很明显利用容器 收集路径字符即可。容器自然而然的消除了字符位置之间的顺序性。
    子序列和子串, 顺序都不可变。很明显,指针顺序遍历即可,按照原序列遍历,具备顺序性。


各有各的玩法, 各有各的规则和处理办法.


滑动窗口基础:
1. 指针一前一后, 维护一个区间, 覆盖前进. 前指针一直向后试探, 慢指针根据题目要求维护窗口(区间大小)
2. 窗口滑动的过程中收集结果. 得出ans

开干切题

光说不练,纸上谈兵是无效的。所以刚说的那么多还是需要应用于实际的代码中.

无重复字符的最长子串
力扣

题目要求很明显了,不可以包含重复字符,采取set作为存储窗口区间元素可以,采取数组映射也可以。(维护区间越长越棒棒哒,唯一要求,不可出现重复,出现重复就需要收集结果,收缩区间。)

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ans = 0;
        unordered_set<int> dict;
        int last = 0, front;
        for (int front = 0; front < s.size(); front++) {
            char ch = s[front];                   //放入窗口的元素.
            while (dict.find(ch) != dict.end()) { //出现重复, 需要收缩区间.
                char delch = s[last];
                dict.erase(delch);
                last ++;
            }  
            dict.insert(ch);
            ans = max(ans, front - last + 1);//获取ans
        }
        return ans;
    }
};


字符串的排列  (无序性 + vector数组映射 + 元素不变性)    排列: 顺序可以打乱. 元素不变性字符不变, 不可多或少  
力扣

字符串的排列,很明显,可以直接利用数组映射来处理。什么意思,这个题目仅仅包含小写字符。所以,可以直接使用26的数组映射就OK了。字符的排列,说白了与顺序无关,仅仅与数目有关系,只要对应字符数目满足了就OK了,还有一点,排列,不能包含其他字符,所以其他字符出现明显,需要upset整个区间

/*
dict: 模板数组,记录着s1串中的信息
count: 记录着滑动窗口滑动过程中的路径信息.
needcnt: 需要满足要求的的字符数目
cnt: 当前满足要求的字符数目
*/

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        std::vector<int> dict(26, 0), count(26, 0);
        int last = 0, front, needcnt = 0, cnt = 0;
        for (auto & e : s1) {
            needcnt += dict[e-'a'] == 0;
            dict[e-'a'] ++;
        }
        for (front = 0; front < s2.size(); front ++) {
            char ch = s2[front];//即将入滑动窗口字符.
            if (!dict[ch-'a']) {//ch不在要求字符中, 卒, 断了, 一切重置.
                count.assign(26, 0);
                last = front + 1;
                cnt = 0;
                continue;
            }
            count[ch-'a'] ++;//放进来.
            if (count[ch-'a'] == dict[ch-'a']) cnt ++;
            if (needcnt == cnt) {//出现ans
                return true;
            }
            while (front - last + 1 >= s1.size()) {//超出区间长度.
                char delch = s2[last];
                cnt -= (count[delch-'a'] == dict[delch-'a']);
                count[delch-'a'] --;
                last ++;
            }
        }
        return false;
    }
};

找到字符串中所有字母异位词 (子序列问题:顺序性 + 元素可变性) 元素可变性, 中间可以插入

力扣

很明显,是上一道题目的一个升级版本. 几乎一毛一样,起码套路是一样滴。而且这个更好写. 收集答案的时候进行收缩就OK了.

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> dict(26, 0), count(26, 0), ans;
        int last = 0, front, cnt = 0, needcnt = 0;
        for (auto &e : p) {
            needcnt += dict[e-'a'] == 0;
            dict[e-'a'] += 1;
        }
        for (front = 0; front < s.size(); front ++) {
            char ch = s[front];
            if (!dict[ch-'a']) {    //序列打断, 全体重置.
                cnt = 0;
                count.assign(26, 0);
                last = front + 1; continue;
            }
            count[ch-'a'] ++;
            cnt += count[ch-'a'] == dict[ch-'a'];
            while (front - last + 1 >= p.size()) {//收缩窗口, 收集ans
                if (cnt == needcnt) ans.push_back(last);
                char delch = s[last];
                cnt -= (count[delch-'a'] == dict[delch-'a']);
                count[delch-'a'] --;
                last ++;
            }
        }
        return ans;
    }
};

通过删除字母匹配到字典里最长单词

力扣

class Solution {
public:
    string findLongestWord(string s, vector<string>& dict) {
        int n = s.size();
        std::sort(dict.begin(), dict.end(), [](string& s1, string& s2) -> bool {
            if (s1.size() > s2.size()) return true;
            if (s1.size() < s2.size()) return false;
            return s1 < s2;
        });
        int last = 0, front = 0;
        for (int i = 0; i < dict.size(); i++) {//遍历所有的dict寻找ans
            for (front = 0; front < s.size(); front++) {
                if (s[front] == dict[i][last]) {
                    last ++;
                }
                if (last >= dict[i].size()) {
                    return dict[i];
                }
            }
            last = 0;//从新开始找下一个。
        }
        return "";
    }
};

思路:话不多说,这个题目,准确来说,不算是滑动窗口。但是也利用了双指针的技巧。先翻译题意,删除字符得到匹配,说白了就是中间存在多余字符,但是顺序性还在,明显的子序列匹配问题。

子序列匹配,待匹配串和匹配串各一个指针同时走,待匹配串的指针走到末尾即为匹配成功。此题要求匹配尽量长,一样长按照字典序,为了尽量少匹配,所以可以先按照规则排序,再依次遍历寻找最优解.

字符串中的所有变位词
力扣

重复了,上面的异位词一样的. 

最小覆盖子串 
典型的滑动窗口,如果搞懂了上述理论直接套用模板解决.
https://leetcode.cn/problems/minimum-window-substring/description/

class Solution {
public:
    string minWindow(string s, string t) {
        int cnt = 0, needcnt = 0, start = 0, minlen = INT_MAX;
        std::vector<int> dict(128), count(128);
        for (auto &e : t) {//记录目标字符
            needcnt += (dict[e] == 0); //需要满足要求的字符数目.     
            dict[e] ++;
        }
        int last = 0, front = 0;
        for (; front < s.size(); front++) {
            char ch = s[front];
            if (dict[ch]) {   //是其中的元素.
                count[ch] ++;//后续多出来的也无所谓.
                cnt += (count[ch] == dict[ch]);//满足字符要求
            }
            while (cnt == needcnt) {//ans
                if ((front - last + 1 < minlen)) {//跟新ans
                    start = last;
                    minlen = front - last + 1; 
                }   
                //尝试缩小窗口
                char delch = s[last];
                last ++;
                if (dict[delch]) {
                    cnt -= dict[delch] == count[delch];
                    count[delch] --;
                }
            }
        }
        return minlen == INT_MAX ? "" : s.substr(start, minlen);
    }
};

重复的DNA序列
https://leetcode.cn/problems/repeated-dna-sequences/

class Solution {
public:
    vector<string> findRepeatedDnaSequences(string s) {
        int last = 0, front;
        vector<string> ans;
        unordered_map<string, int> dict;
        for (front = 0; front < s.size(); front ++) {
            if (front - last + 1 == 10) {
                //maybe ans
                string tmp = s.substr(last, 10);
                if (dict.find(tmp) != dict.end()) {
                    if (dict[tmp] == 1) ans.push_back(tmp);
                }
                dict[tmp] ++;
                last ++;//剔除元素, 缩小窗口
            }
        }
        return ans;
    }
};

最长公共前缀
https://leetcode.cn/problems/longest-common-prefix/

class Solution {
public:
    //遍历走    
    string longestCommonPrefix(vector<string>& strs) {
        char ch;
        int ind = 0;
        while (1) {
            for (int i = 0; i < strs.size(); i++) {
                if (ind >= strs[i].size()) return strs[i];
                ch = (i == 0 ? strs[i][ind] : ch);
                if (ch != strs[i][ind]) {
                    return ind == 0 ? "" : strs[i].substr(0, ind);
                }
            }
            ind ++;
        }

    }
};

滑动窗口的最大值
https://leetcode.cn/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/

此题也算是一道经典的复合题目,其中还包含了单调栈或者说单调队列的一点单调序列的思维在里面,这个很经典,如果后续又继续希望可以跟大家分享。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        //维护单调队列吧
        std::deque<int> less_que;//单调递减队列
        vector<int> ans;
        int last = 0, front = 0, need = INT_MIN;//need记录需要值
        for (; front < nums.size(); front ++) {
            while (!less_que.empty() && nums[front] > less_que.back()) {
                less_que.pop_back();
            }
            less_que.push_back(nums[front]);
            if (front - last + 1 == k) {//收集结果, 缩小区间
                ans.push_back(less_que.front());
                if (nums[last] == less_que.front()) {
                    less_que.pop_front();
                }
                last ++;
            }
        }
        return ans;
    }
};

长度最小的子数组
https://leetcode.cn/problems/minimum-size-subarray-sum/?envType=study-plan-v2&id=top-interview-150

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ans = INT_MAX;
        int sum = 0;
        int last = 0, front = 0;
        for (int front = 0; front < nums.size(); front ++) {
            sum += nums[front];//放入窗口中
            while (sum >= target) {//收集答案并且尝试不停缩小左窗口, 收集最优ans
                ans = min(ans, front - last + 1);
                sum -= nums[last];//last出窗口
                last ++;
            }
        }
        return ans == INT_MAX ? 0 : ans;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/450816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

总结:Grafana Mimir调用

一、背景 Prometheus单实例&#xff0c;可用性低&#xff0c;可靠性低&#xff0c;不能存储更多数据。 解决业务问题 如&#xff1a;当前QKE是一个集群一个项目一个prometheus实例&#xff0c;那么当我一个应用分多个集群部署的时候&#xff0c;查询数据时就得从三个promethe…

streamlit (python构建web可视化框架)笔记

文章目录 一、安装使用streamlit二、streamlit使用1.展示和数据样式2.dataframe()生成交互表和table()方法生成静态表3.绘制折线图4.绘制地图5.一些组件slider()滑动条 checkbox()确认框 selectbox()选择器6.侧边栏7.布局分列8.多页 三、Steamlit可视化简单应用--冒泡排序可视化…

java获取当前系统时间

在Java中&#xff0c;可以使用以下几种方法获取当前系统时间&#xff1a; 方法1&#xff1a;使用java.util.Date类 java import java.util.Date; public class Main { public static void main(String[] args) { Date date new Date(); System.out.println("当前时间&…

短视频app开发:如何设计个性化推荐算法

短视频app的迅速崛起已经成为了移动互联网领域中的一股热潮。然而&#xff0c;如何设计个性化推荐算法已经成为了这个领域中的一个核心问题。在本文中&#xff0c;我们将深入探讨如何为短视频app开发设计个性化推荐算法&#xff0c;以及如何使用短视频源码来实现这一目标。 简…

类间关系和内部类和数组

Final关键词 定义Pepole类&#xff0c;运用了final修饰方法eat()&#xff0c;该方法不能被改写&#xff0c;但可以随类进行继承。 用final修饰的类&#xff0c;不能有子类。 内部成员类定义方式 外部类.成员类 对象名 new 外部类&#xff08;&#xff09;.new 内部类。 局部…

[附源码]计算机毕业设计基于SSM和UNIAPP的选课APP

项目初衷 教育要实现现代化&#xff0c;高质量发展&#xff0c;就必须拥抱互联网。在此推动下&#xff0c;教育APP软件的开发非常受欢迎。通过APP自主选择教育课程的专业和课程&#xff0c;教授讲课&#xff0c;课程APP可以在线合作。通过APP自主选课的方式&#xff0c;更能激…

深度强化学习——actor-critic算法(4)

一、本文概要&#xff1a; actor是策略网络&#xff0c;用来控制agent运动&#xff0c;你可以把他看作是运动员&#xff0c;critic是价值网络&#xff0c;用来给动作打分&#xff0c;你可以把critic看作是裁判&#xff0c;这节课的内容就是构造这两个神经网络&#xff0c;然后…

项目结束倒数2

今天,解决了,多个点的最短路问题 用的dfs,配上了floyed计算出的广源距离 难点是要记录路线,dfs记录路线就很烦 但是好在结束了,经过无数的测试,确保没啥问题(应该把) 来看看我的代码 void dfs(int b[], int x, int* sum, int last, int sums, int a[], BFS& s, Floyd_A…

Java核心技术 卷1-总结-14

Java核心技术 卷1-总结-14 映射更新映射项弱散列映射链接散列集与映射枚举集与映射 视图与包装器轻量级集合包装器 映射 更新映射项 处理映射时的一个难点就是更新映射项。正常情况下&#xff0c;可以得到与一个键关联的原值&#xff0c;完成更新&#xff0c;再放回更新后的值…

【二叉树】遍历二叉树

前言 二叉树有前中后序和层序四种常用的遍历方式&#xff0c;今天我们来学习一下如何用这四种方法遍历二叉树。 前序&#xff1a;根、左、右 中序&#xff1a;左、右、根 后序&#xff1a;左、右、根 层序&#xff1a;第一层、第二层… 递归 递归是一种将复杂问题不断细分成…

RHCE(五)

目录 一.判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间 1.创建脚本test1.sh 2.下载邮件服务并执行 3.测试 4.做计划任务 二.判断web服务是否运行&#xff08;1、查看进程的方式判断该程…

ChatGPT 速通手册——模仿唐诗宋词,和模仿莎士比亚十四行诗的中英文差距

模仿唐诗宋词&#xff0c;和模仿莎士比亚十四行诗的中英文差距 根据前文介绍的三大反例特性&#xff0c;我们可以尝试给出几个典型的反例。比如诗词创作&#xff0c;尤其是长短句约束更加严格的词牌&#xff0c;对照反例特性&#xff1a; 有明确且唯一可行的标准定义——一个…

Windows OpenVino安装squeezenet1.1失败 —— 已解决

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,YOLO,python领域博主爱笑的男孩。擅长深度学习,YOLO,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typecollec…

【排序】冒泡排序与快速排序(三个版本+非递归图示详解哦)

全文目录 引言冒泡排序快速排序思路实现Hoare版本快排优化 挖坑法前后指针法 快排非递归版本思路实现 总结 引言 在这篇文章中&#xff0c;将继续介绍排序算法&#xff1a;冒泡排序与快速排序&#xff1a; 它们都属于交换排序&#xff0c;即通过两两比较交换&#xff0c;将较…

小朋友崇拜圈+灌溉(JAVA解法)

目录 小朋友崇拜圈 题目链接&#xff1a; 题目描述 输入描述 输出描述 输入输出样例 灌溉 题目链接&#xff1a; 题目描述 输入描述 输出描述 输入输出样例 小朋友崇拜圈 题目链接&#xff1a; https://www.lanqiao.cn/problems/182/learning/?page5&first_c…

手撕源码(一)HashMap-JDK8

目录 1.使用示例2.new HashMap<>() 解析2.1 加载因子2.2 构造方法 3.put() 解析3.1 原始put(k, v)3.2 计算哈希1&#xff09;为什么要进行二次hash&#xff1f;2&#xff09;二次hash计算示例&#xff1a;3&#xff09;为什么使用 (length-1)&hash 而不是 hash%lengt…

互联网医院系统|线上问诊系统定制|互联网医院源码开发技术

当下医疗成为人们比较关注的问题&#xff0c;移动医疗技术不断的进步&#xff0c;互联网医院系统成为了医疗企业发展的必经之路&#xff0c;通过移动终端与互联网医院系统进行连接&#xff0c;实现医疗服务的远程交互与管理。可以使医疗机构的医生通过移动设备随时随地的查看患…

词的表示方法笔记——词向量+代码练习

词的表示方法&#xff1a; 一、one-hot&#xff08;最简单&#xff09; 独热编码是一种将单词转化为稀疏向量的方法&#xff0c;其中每个单词都表示为一个只有一个元素为1其余元素均为0的向量&#xff0c;其维度由词库的大小决定。。例如&#xff0c;对于包含 4个单词的词汇表 …

Python二分查找(折半查找)的实现

时间复杂度 最优时间复杂度&#xff1a;O(1) 最坏时间复杂度&#xff1a;O(logn) 思路 对有序的顺序表进行查找&#xff0c;以下标查找&#xff0c;每次取一半查找&#xff0c;如[1,2,3,4,5,6,7,8,9]&#xff0c;查3, 下标从0~8&#xff0c;(08)//24,对比折半到下标为2的元素…

【基础算法】栈和队列

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招算法的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于代码随想录进行的&#xff0c;每个算法代码参考leetcode高赞回答和…