day2_leetcode3

Leetcode第三题
1. 题目:
    

2.   先普及两个概念。子串和子序列
        子串:就是将字符串从某个地方连续的切片得到的字符串(重点关注连续)。
        子序列:就是将字符串中的任意字符重新组合得到的序列(但是元素之间的顺序不能变)。
        举个例子说明一下。
        这儿给定一个字符串string=”abc”那么它的子串就有”a”,”b”,”c”,”ab”,”bc”,”abc”,空串。子序列有      ”a”,”b”,”c”,”ab”,”bc”,”ac”,”abc”,空串。注意到区别了吗,子串必须是原来字符串中连续的一个或几个字符串构成,相当于步长为1的切片操作得到的字符串string[x:y:1]。而子序列就是可以用任意正数步长的,即保证字符之间的顺序没有更改。比如”ba”就不是string的子序列,因为顺序变了。

3. 第一思路,就是暴力分析。先贴代码再分析吧。

    

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        str_dict = {}
        for index in range(0, len(s)):
            string = ""
            for count in range(index, len(s)):
                if s[count] in string:
                    break
                string += s[count]
            str_dict[string] = len(string)
        row = []
        for v in str_dict.values():
            row.append(v)
        try:
            return max(row)
        except ValueError:
            return 0


        定义空字典,用来装之后每次循环构成的最长的字符串以及它的长度。例如str_dict = {”abc”,3}。
第一层循环,确定每次循环的第一个字符,然后定义一个string变量,这个string就是以当前字符开头的最长的字符串。在第二层循环中,从当前的字符往后遍历,如果之后遇到的字符不在string中,则说明还没有遇到重复的,那么就可以继续判断下去,直到遍历到的字符在string中,那么这次循环就可以结束了。str_dict[string] = len(string)这段代码就是在每次结束之后,将这次循环得到的最长字符串和它的长度存入字典。然后进行下一次循环……
        结束之后,每次循环的结果就在str_dict中了,因为题意是要将最长字符串的长度返回,所以我只需要将字典中的值(每个子串的长度),放入列表,然后利用max(list)就可以得到其中的最大值,返回就行了。
        但是这儿要注意一个问题,就是max()方法的使用,如果传入的列表长度为0,那么找不到最大值的话,就会报错,所以我用一个异常处理来解决这个问题。其实也可以这样,传入的列表为什么是0呢?它不是str_dict中每个子串的长度吗?答案是假如传入的原始的字符串s是一个空字符串呢,那就得不到任何的子串了。所以如果不想用异常处理,那就在最开始判断传入的s是否是一个空字符串,如果是,那就直接返回0就行了。
重点来了,来看看运行结果:
          
          执行用时508ms,内存消耗13.3MB,基本操作执行次数函数T(n) = n(n-1)*n,时间复杂度O(n) = n^3。结果不太理想。

4.  现在来优化一下,看一下能不能减少一点循环。看上面这段代码,第三个for循环应该是可以想办法去掉的。现在已经可以从第1,2个for循环得到每次循环的最长子串。那么我不就可以在第1,2个for循环中将最长子串放在一个列表而不是字典中,然后直接利用max()方法得到最长子串返回不就可以了吗。这样可以减少一个循环。所以我又写了下面这段代码:
    

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        if len(s) == 0:
            return 0
        length_list = []
        for index in range(0, len(s)):
            string = ""
            for row in range(index, len(s)):
                if s[row] in string:
                    break
                string += s[row]
            length_list.append(len(string))
        return max(length_list)


执行结果如下:
     
     ???????我不是在之前的基础上减了一个循环吗,怎么时间反而会增加了,难道是python中对字典的遍历比列表真的会快很多?不行,那我就想办法不用这个列表。我的列表不是用来将每次循环的最长子串的长度存在来吗,那我现在不这样了,我直接每次循环结束判断当前的长度是不是比之前的长,如果是,那就让最长子串的变量等于它,这样就可以不用使用列表,也可以不用max()这个方法了。所以下一个版本的代码如下:
      

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        if len(s) == 0:
            return 0
        max_length = 0  # 默认等于0
        for index in range(0, len(s)):
            string = ""
            for row in range(index, len(s)):
                if s[row] in string:
                    break
                string += s[row]
            max_length = len(string) if max_length < len(string) else max_length  # 保证max_length是最长子串的长度
        return max_length


执行结果如下:

     
wtf?????,玩呢?是我现在打开的网页太多了,还是打开的应用太多?或者就是我做的这些其实对程序本身的影响就不大。但是这几个的基本操作执行次数函数T(n) = n(n-1),时间复杂度O(n) = n^2。看来要突破这个限制还是要在减少时间复杂度才行。现在想想办法能不能把时间复杂度降到n呢?

5.  找了相关的答案,发现一个很好的思路。时间复杂度是O(n) = n,先把我代码和执行结果放出来吧:
代码(看了大神的思路之后写的):
     

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        max_length, string = 0, ””
        for row in s:
            if row not in string:
                string += row
            else:
                max_length = len(string) if max_length < len(string) else max_length  
                # 保证max_length是最长子串的长度
                string = string[string.index(row)+1:]+row
        max_length = len(string) if max_length < len(string) else max_length  
        # 保证max_length是最长子串的长度
        return max_length


       执行结果:
     

       看人家这个效果,大神是真的大神,佛了。现在说一下思路吧。
       我们只需要一路遍历,并将遍历到的字符加入string中。当遍历到的字符在string中,说明遇到重复的了。比如当前的string=”abcd”, 而当前遍历到的字符是”b”, 那么我们可以将max_length与len(string)对比, 哪个大,就让max_length等于哪个,保证max_length一直都是表示遍历到的最长子串的长度。
        然后,因为此时的“b”与string中的”b”重复了,所以我们就要将此时的string切片,将string中”b”以及之前的字符都切掉。即变成string=”cd”,然后再将此次遍历到的”b”加在最后,即最终构成string=”cdb”,之后再继续遍历下去,知道遍历到字符串s的最后一个字符结束。
       为什么在for循环外又判断了一次最长子串呢?因为假如遍历到s的最后一个字符后,这个字符并没有出现在string中,那么它就执行的是if中的语句,但是我们并没有对此时的string的长度作比较,所以在外面还需要判断一下。


 

根据引用,可以看出这是一段使用迭代器进行循环遍历的代码,并且遍历的对象是`vector<int>`类型的向量。迭代器`it`初始化为`c.begin()`,结束条件为`it < c.end()`。这段代码中省略了循环体,需要根据具体上下文来确定循环体的具体操作。 根据引用,我们可以了解到`vector`是一种动态数组,可以存储多个相同类型的元素。代码示例中用`assign`函数将另一个向量的一部分元素赋值给目标向量。具体来说,`a.assign(b.begin(), b.begin()+3)`意味着将向量`b`的前三个元素赋值给向量`a`。 根据引用,可以看出这是一段关于在VSCode中安装leetcode插件和配置的说明文档。文档中提到了如何安装插件、如何编译和构建代码等内容。 综上所述,LeetCode的`vector`是一种动态数组,可以存储多个相同类型的元素。可以通过迭代器对其进行循环遍历,也可以使用成员函数`assign`来赋值部分元素。在VSCode中,可以安装LeetCode插件并进行相关配置来进行编译和构建。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [C++LeetCode每日一题 Day3 常用容器vector的使用](https://blog.csdn.net/m0_75276704/article/details/129737396)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [vscode安装leetcode-Leetcode:力码](https://download.csdn.net/download/weixin_38557935/20051243)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大骨熬汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值