不容易解的题10.7

news2024/9/23 5:15:30

885.螺旋矩阵III

885. 螺旋矩阵 III - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/spiral-matrix-iii/?envType=list&envId=ZCa7r67M模拟题的一种,说难也难,说简单也简单。模拟题有很多套路题,它们的题解差不多,但是也有一些是没做过,不容易想出来的,或者说能想到大概思路,但不容易想全面的。

这道模拟螺旋矩阵III是要我们返回,模拟矩阵的螺旋遍历时,每次经过的给定矩阵的坐标都是什么

题解思路:

根据题目中图的示例我们可以观察出,每两次调转方向内走的步数是相等的,且每次走完当前方向的最大步数,调转一次方向,且每调两次方向后,所能走的最大步数加1。

观察出这些后,问题才能迎刃而解

创建一个二维数组around来记录本次方向下一步往哪走。然后创建上面观察出的需要的各个变量,即当前调转了几次方向,当前的方向,当前走的步数,当前方向最大步数,还要有两个变量xy,它们代表了当前遍历到的位置。

注意xy是可以出矩阵边界的,我们只需要模拟就可以了,这和题目给出的图示完全相符,我们看当前xy位置是不是在矩阵里,如果是,将它录入到返回数组中,如果不是接着走就可以了。

循环结束条件:用一个变量num记录当前已经走过了矩阵的几个数,当num==矩阵行×列,即可退出循环,表明该矩阵所有部分全走完了,返回记录的数组。

难点:没做过,很难想到用around数组辅助记录各方向上x和y的变化取值,和如何用方向去使用around数组,循环结束条件是什么?如果没做过类似题,甚至都不会注意,每调转两次方向会使最大步数增加。总之不要小看模拟题,有时候模拟题,也不是很容易的想出

class Solution {
public:
    vector<vector<int>> spiralMatrixIII(int rows, int cols, int rStart, int cStart) {
        vector<vector<int>>res;
        int maxc=1,count=0,dir=0,cdir=0;
        vector<pair<int,int>>around={{0,1},{1,0},{0,-1},{-1,0}};
        int num=1,x=rStart,y=cStart;
        while(num<=rows*cols){
            if(x>=0&&x<rows&&y>=0&&y<cols){
                res.push_back({x,y});++num;
            }
            if(count==maxc){
                cdir++;count=0;dir++;
            }
            if(cdir==2){
                cdir=0;maxc++;
            }
            x+=around[dir%4].first,y+=around[dir%4].second;count++;
        }
        return res;
    }
};

方向只有上下左右四个方向,图示我们看的出来每次必定向右走然后向下走......所以dir自增后超出了4模4可以使它仍然正确的去遍历。

当然你也可以在调转三次方向后,把方向变成0,重新开始,然后弄出几个循环去连续判断,此时dir对应哪个方向,再去调整xy下一次坐标,很多其他题解采用了这样的思路,但本质上依旧脱离不开模拟,且是相同的思路。

递归应该也可以,相同的思路,然后把每次要用到的变量做参数传到下一次递归里,但是我没怎么写过递归,而且可能会很慢,这里不再展开说明。


14.最长公共前缀

14. 最长公共前缀 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-common-prefix/?envType=list&envId=ZCa7r67M官方题解的横向遍历纵向遍历实际上我认为就是广搜和深搜的思想。

这里都分析一下:

先说纵向遍历,我本人喜欢用这种方法,记的也是这种方法。(其实不用一道题非要记很多种解法,记住一种能方便刷题,也能加深印象,当然你是大佬当我没说吧)

纵向遍历的思想就是字面意思,以第一个字符串为模板,挨个拿第一个字符串的各个字符去和其他的字符串的对应下标字符去比较,也就是一列一列比较。如果不匹配,直接return即可,因为我们这道题求得是最长公共前缀,不是最长公共子串,所以以开头到这个字符为止只要有字符不等,则不该再遍历了,直接返回当前最大子串即可,这里要注意的是前缀这一特性。

说说剪枝:这道题是可以剪枝的,事实上也必须要这样做。

假如该字符串数组中有某一个字符串长度很短,至少它短于第一个字符串,那么我们在访问该字符串的这个对应下标很可能造成下标越界访问,所以判断一下如果此时要检查的下标i已经和该次要判断的字符串长度都相等了,那么也直接return就可以了。这里要注意的是公共这一特性。

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if(strs.size()==1)return strs[0];
        string s="";
        for(int i=0;i<strs[0].size();++i){
            char ch=strs[0][i];
            for(int j=1;j<strs.size();++j){
                if(strs[j].size()==i||strs[j][i]!=ch){return s;}
            }
           s=strs[0].substr(0,i+1);
        }
        return s;
    }
};

这里还可以直接拿一个字符串s2保存strs【0】,这样每次判断的是s2【i】对应的字符,有些题解就是这样写的,这只是把二维书写转为一维了思路是完全一样的。

再来看看横向遍历:

思想就是两个字符串两个字符串比较,那么是哪两个字符串呢?是滑窗那样的两个字符串依次比较吗?那肯定不是,我们还是拿第一个字符串做样例串,一个函数去判断该次遍历到的字符串和样例串截至到哪个字符不等,不等跳出函数循环,然后截取样例串作为下一次判断的样例串,样例串越截越短,它代表的是上一次的求得的最长前缀公共串的结果,将该结果与其他所有字符串相比较,最后把它们共有的公共前缀返回去即可。

class Solution {
public:
string get(string&s1,string &s2){
    int c=min(s1.size(),s2.size());
    int index=0;
    while(index<c&&s1[index]==s2[index]){
        index++;
    }
    return s1.substr(0,index);
}
    string longestCommonPrefix(vector<string>& strs) {
        string res=strs[0];
        if(!strs.size())return "";
        for(int i=1;i<strs.size();i++){
            res=get(strs[i],res);
        }
        return res;
    }
};

上面是官方代码的实现,再贴一个自己实现的横向遍历

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if(!strs.size())return "";
        string res=strs[0];
        for(int i=1;i<strs.size();i++){
            int count=min(res.size(),strs[i].size());
            int j=0;
            for(;j<count;j++){
                if(strs[i][j]!=res[j]){break;}
            }
            res=strs[i].substr(0,j);
        }
        return res;
    }
};
//["ab", "a"]注意这个用例

思路是相同的,我们这里用的是count来帮助判断上一次最长前缀公共子串到哪截止,而不是像上一个题解一样用截取的来判断,但思路是一样的,不要忘记把j写外面,这样有助于我们保存答案,

两点需要注意:
里层循环更新数据,用来比较当前字符串字符各个位置和前缀字符串的
剪枝方法是以两者较小长度作为循环判断结束条件
第二点,一旦不符合,要break跳出来,再进行前缀字符串的更新!!!
这是避免测试用例【a,b】和【a】这种情况发生

这种情况由于我们使用最小长度判定,所以即使前缀是a也会返回ab
因为循环根本进不去,所以一定要在里层循环外面更新

最好按官方题解写更加简洁,且不容易出错,这种写在一起很容易出错


3.无重复字符的最长字串

3. 无重复字符的最长子串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-substring-without-repeating-characters/?envType=list&envId=ZCa7r67M求最长子串并且是不含重复字符的最长字串。

我们一点点分析这道题,就会发现这道题没有想象中那么难

首先无重复如何实现?使用哈希表

最长字串如何实现?它不是前缀串,所以不能只有一个变量记录截至到哪符合条件,而且要求的是子串,子串是什么?连续的啊,自然而然想到滑窗

这是一道很好的典型的哈希表搭配滑窗解决的问题。

这里给出代码实现思路:

代码思路是用一个循环,循环变量是i,但我们并不让循环自动推进i,而是它只起到判断i是否超出字符串范围的作用,这里i相当于右窗口。其实这里写成一个while也一样。然后外面一个变量left记录此时子串从哪个下标开始,里层循环进来就判断,如果循环变量i大于0,也就是说不是刚走进来的,那么直接删掉哈希表中此时子串的头元素,也就是left指向的那个,然后left++,这是为什么呢?

这句判断后面跟的代码是一层里层循环,它是实现推进右窗口的,不停推右窗口,只有两种情况会停止,一是越界,二是此时我们的子串右窗口已经遍历到了一个重复的字符,这时候停止推进右窗口,取答案后,再进行外层循环的,if判断,删掉一个左窗口元素后,再推进右窗口,如果此时仍然是哈希表里能找到该右窗口的元素,那么继续缩减左窗口。

这道题我会有多个方法来说,让大家更深刻的理解这道题的各种代码写法

先解答一些疑问:

为什么我们不用判断窗口此时是否含有相等的字符就取答案?用了什么操作使答案能保持正确?

为什么思路看起来这么奇怪,如果是外层循环控制右窗口的扩大每次进行++,然后里层循环if判断如果此时子串遍历过程中存在了重复字符的问题,再使用上述的删除left字符的方法,left++行不行?

为什么我们删除的是最左边字符?重复的字符很有可能不在最左边啊?

比如对于测试样例【pwwkew】我们完全可以删除本次找到的那个重复字符之前的所有字符,然后以本次的w为左窗口之后子串扩大窗口,而且该字符之前的最长情况已经被答案所记录,也不用担心我们错过最长子串的情况,还能提高效率,为什么不这么写?

下面我将对以上问题做出一一解答,并告诉大家代码都可以怎么写,而怎么写看起来是对的,但实际上有大漏洞,听我慢慢刨析。

第一个问题,和我们的答案取值有关看代码

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res=0;
        if(s.size()<=1)return s.size();
        unordered_set<char>set;int left=0;
        for(int i=0;i<s.size();){
            if(i>0){set.erase(s[left]);left++;}
            while(i<s.size()&&set.find(s[i])==set.end()){
                set.insert(s[i]);++i;
            }
            res=max(res,i-left);
        }
        return res;
    }
};

这样的代码取值保证了我们只会取到最大子串长度,而我们第一次因为字符重复退出时,此时的右窗口下标对应的下标减去左窗口对应的下标刚好等于窗口长度而不用+1,这个大家应该都能理解,这就是恰好超过一个,你想想如果该窗口的答案正好是最长的不重复子串,你的i-left得到的值是不是比窗口大小小1?这是下标从0走导致的

再看,我们已知它会因为此时i向后遍历找到字符与此子串中某字符重复时候,就无法进入循环,直到上面的if删除把前面的重复字符删掉后,才能进来,所以不用担心,这一过程越取越小,不会吧错误答案加进来。

第二个问题外层循环控制右窗口每次++,而里层循环每次用if去判断是否有重复字符,有就删除这个方法是非常错误的,也是很容易陷入误区的。

我们可以用while来判断,但是绝不能用if,if有很大的副作用!

如果此时的用例中,当前的右窗口遍历的字符与该窗口现在的某字符相等需要去重时,if只能去除一个,如果此时去除掉的并不是我们应该去除的,而后右窗口接着往后走,这将导致此次遍历到的情况很有可能已经不重复了,然后答案记录下来,但是此时子串真的已经没有重复字符了吗?这很不一定,这里使用if会导致一个很隐晦的逻辑错误!

代码错误容易修改,逻辑错误很难察觉!

这里正确做法应该使用while去不断左移,直到此时子串一定没有重复字符为止。

第三个问题如果我们删除的字符是在左边,这很有可能不是我们要删除的字符,这是没毛病的。

但是不要忘了,我们手动控制右窗口滑动,这就是好处,当此时子串中还存在重复字符时候,窗口一定不会增大,直到此时窗口没有重复字符,这其实和上面说的思路是一样的。

第四个问题该方法的确能够提升一些效率,看起来好像没什么错误,但实际上有错误,该方法只能够处理答案在测试样例的开头和中间的情况,对于答案出现在末尾比如【dvdf】这样的案例无法处理。

对于第二点我还要说明一下,我举个例子说明对于测试用例【bbbbb】

你可以对照上面最开始的正确代码,你不要妄想把代码写成如果当前遍历字符是set出现过的,那么就直接删掉left然后left++,这种代码在这种思路里是是十分愚蠢的。
就拿用例【bbbb】而言,你去删掉set中元素,而此时又没有新加元素,则会导致当前字符被漏掉
那如果你写成else里是删除left对应字符此时恰好在set里,然后left++并且把当前数据加进来呢?
那我只能说,这个方法还算比之前聪明,但是它仅能通过以尾字母结尾的子串长度,和中间能找得到的子串答案的用例,以首部开头的你找不到,因为外层循环这里的思路是i控制右窗口一直在++,你这里还没删完,就很快跳出循环来
如果循环控制右窗口的前提下,你想解决这种问题,可以试着把删除变成循环,就是当前字母如果一直能在set里找到,一直删s【left】
要不然你就把思路实现为while循环自己手动控制右窗口滑动,
或者再有一种思路就是循环控制左窗口,然后r手动控制右窗口,也是循环,一直找,一直扩大右窗口直到发现set出现该数,删除左窗口数字,这时候外层循环i控制左窗口,i行动比我们慢得多,因为我们手动控制右窗口滑动,直到找到set里出现才停下的,此时虽然不是while删除左边,但是有很充裕时间会删完直到set里没有重复数
当然你用两个while分别控制左右滑窗也是行的

总之是必须要写循环控制窗口,这是重中之重、。

那如果你非要使用if来完成对于两个窗口的滑动就一定没有方法了吗?

那还是有的while(1)写在外面,然后一点点移动左右窗口吧

直到找完整个字符串后再break或者return跳出去

由于已经说了这么多了,而且这些实现只是代码细节有所不同,但是实现思路相似,这里不给出其他对应代码,感兴趣可以自己实现一下


都看到这里了如果对您有用的话别忘了一键三连哦,如果是互粉回访我也会做的!

大家有什么想看的题解,或者想看的算法专栏、数据结构专栏,可以去看看往期的文章,有想看的新题目或者专栏也可以评论区写出来,讨论一番,本账号将持续更新。
期待您的关注

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

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

相关文章

Vue 中 KeepAlive 内置缓存使用

KeepAlive 介绍及使用场景 KeepAlive 是 vue 中的内置组件&#xff0c;当多个组件动态切换时可以对实例状态进行缓存&#xff0c;用法如下 <router-view v-slot"{ Component }"><keep-alive><component :is"Component" /></keep-al…

Adobe Photoshop2018-2024mac/win合集:轻松掌控设计世界

Adobe Photoshop&#xff0c;简称“PS”&#xff0c;是由Adobe Systems公司开发和发行的图像处理软件。Adobe Photoshop主要处理以像素所构成的数字图像。使用其众多的编修与绘图工具&#xff0c;可以有效地进行图片编辑工作。 Adobe Photoshop在图像、图形、文字、视频、出版等…

京东商品数据:8月京东环境电器行业数据分析

8月份&#xff0c;环境电器大盘市场整体下滑。鲸参谋数据显示&#xff0c;8月京东平台环境电器的大盘将近570万&#xff0c;环比下滑约29%&#xff0c;同比下滑约10%&#xff1b;销售额为25亿&#xff0c;环比下滑约23%&#xff0c;同比下滑约8%。 *数据源于鲸参谋-行业趋势分析…

数字经济和法治背景下国企合规数字化转型思考

近年来&#xff0c;数字经济的快速发展已经深刻影响了各行各业的运营方式和商业模式。在这个数字化时代&#xff0c;企业要想保持竞争力和可持续发展&#xff0c;必须紧跟时代潮流&#xff0c;进行数字化转型。而对于国有企业来说&#xff0c;数字化转型中的合规问题显得尤为重…

景联文科技:AI大模型强势赋能,助力自动驾驶迭代升级

我国一直以来都将自动驾驶作为新兴产业发展的重点领域之一&#xff0c;工信部等相关部委出台了一系列自动驾驶发展战略、规划和标准&#xff0c;一些地方政府也在积极开展关于自动驾驶的地方立法&#xff0c;为自动驾驶技术的研发和应用提供更加具体的法律保障。例如&#xff0…

记录:Unity脚本的编写3.0

目录 前言前置控制方法查看效果移动方式 前言 前面记录了一些通过脚本控制对象模型移动和通过用户的操作对模型进行变化的方法&#xff0c;那么为了让我们创造的不论是地形还是模型都拥有真实的物理引擎&#xff08;大雾&#xff09;&#xff0c;那么这次就使用脚本控制模型感…

purr map walk 学习教程 完整版

Function reference • purrrhttps://purrr.tidyverse.org/reference/index.htmlMap over multiple input simultaneously (in "parallel") — pmap • purrr

linux python 保存图形savefig import matplotlib.pyplot as plt

import matplotlib.pyplot as plt # 绘制图形 mod.plot_history(20)# 保存图形 plt.savefig("my_training_ephoes_plot.png") # 保存为PNG格式 # 保存图形并设置dpi参数 plt.savefig("my_plot.png", dpi600) # 保存为PNG格式&#xff0c;设置dpi为300

Conv1d与Conv2d函数用法

Conv1d 和 Conv2d 分别是卷积神经网络&#xff08;CNN&#xff09;中的两种卷积层操作&#xff0c;它们在处理不同维度的数据上有联系和区别&#xff0c;本文是一篇学习笔记。 本文主要包括以下内容&#xff1a; 1.联系2.区别3.Conv1d卷积4.Conv2d卷积5.图解Conv1d卷积&#x…

一文告知HTTP GET是否可以有请求体

HTTP GET是否可以有请求体 先说结论&#xff1a; HTTP协议没有规定GET请求不能携带请求体&#xff0c;但是部分浏览器会不支持&#xff0c;因此不建议GET请求携带请求体。 HTTP 协议没有为 GET 请求的 body 赋予语义&#xff0c;也就是即不要求也不禁止 GET 请求带 body。大多数…

教资一年可以考几次 教资考试每年次数介绍

教师资格证一年可以考两次。根据教师资格证考试规定&#xff0c;为了满足报考人员的工作需求&#xff0c;达到市场供求均衡的状态&#xff0c;教师资格证区别于其他的技术资格类的考试&#xff0c;会每年举行两次考试&#xff0c;分别在上半年和下半年各举行一次考试。 上半年…

设计模式 - 行为型模式考点篇:策略模式(概述 | 案例实现 | 优缺点 | 使用场景)

目录 一、行为型模式 1.1、策略模式 1.1.1、概论 1.1.2、案例实现 1.1.3、优缺点 1.1.4、使用场景 一、行为型模式 1.1、策略模式 1.1.1、概论 策略模式设计的每一个算法都封装了起来&#xff0c;使他们可以相互替换&#xff0c;通过一个对象委派不同的算法给相应的客户…

【网络安全】关于CTF那些事儿你都知道吗?

关于CTF那些事儿你都知道吗&#xff1f; 前言CTF那些事儿内容简介读者对象专家推荐 本文福利 前言 CTF比赛是快速提升网络安全实战技能的重要途径&#xff0c;已成为各个行业选拔网络安全人才的通用方法。但是&#xff0c;本书作者在从事CTF培训的过程中&#xff0c;发现存在几…

Ant Design of React 创建项目及运行环境

Ant Design of React 学习笔记&#xff08;1&#xff09; 创建项目及运行环境 本文使用 create-react-app 创建一个 TypeScript 项目&#xff0c;并引入 antd。 1.命令行cd到对应的目录&#xff0c;这里是npm下回车运行&#xff0c;其他如pnpm yarn参考官网 npx create-reac…

Java实验(头歌) -Java继承和多态接口

/*** 编写程序&#xff0c;实现两个数的求和运算和比较*/ // 请在下面的Begin-End之间按照注释中给出的提示编写正确的代码 /********** Begin **********/ // 定义一个接口类 Compute// 第一个为 sum()&#xff0c;实现两个数的相加&#xff0c;返回值为 int// 第二个为 max()…

【软件测试】APP 上架指南:iOS App Store 首次上架被拒原因分析与解决方案

目录 一、前言 二、APP 审核备案新规 &#xff08;1&#xff09;iOS 上架审核申请被拒 &#xff08;2&#xff09;苹果应用商店重大调整 &#xff08;3&#xff09;首次备案流程 ① 阿里云备案 ② 华为云备案 ③ 腾讯云备案 三、iOS 首次上架拒审原因分析 &#…

动捕设备推动舞蹈表演动作捕捉动画制作突破边界

动捕设备是音乐动画制作中获得巨大创作潜力的一种全新途径。使用全身动捕设备可以花更少的时间编辑预制动画资产&#xff0c;从而节省时间投入到创建更为丰富的动画制作环境中。 如广州虚拟动力对于舞蹈表演动作捕捉动画制作服务&#xff0c;能够提供惯性动捕设备vdsuit full以…

ctfshow-web2(SQL注入)

打开题目链接&#xff0c;是一个登录框&#xff0c;尝试弱口令登录 没什么反应 尝试万能密码登录 页面发生了变化 并未登录进去&#xff0c;但是有回显&#xff0c;大概率是SQL注入了 这里尝试了很久&#xff0c;尝试过程就不写了&#xff0c;最终判断它的注入点在用户名&…

C++ 类和对象篇(七) 运算符重载

目录 一、运算符重载 1. 运算符重载是什么&#xff1f; 2. 为什么要运算符重载&#xff1f; 3. 怎么进行运算符重载&#xff1f; a. 运算符重载函数的格式 b. 参数的个数和要重载的运算符操作数相同 c. 运算符重载函数必须有一个类类型参数 4. 注意事项 二、 赋值运算符重载 1.…

提升企业形象与效率,申请办理400电话客服中心

引言&#xff1a; 随着企业竞争的日益激烈&#xff0c;提供优质的客户服务成为企业赢得市场份额的关键。而400电话客服中心的建立&#xff0c;不仅可以提升企业形象&#xff0c;还能提高客户满意度和工作效率。本文将介绍400电话客服中心的申请办理过程&#xff0c;以及其对企业…