力扣刷题日记

本文介绍了多种方法解决力扣中的一个问题:找到字符串中不含重复字符的最长子字符串的长度。方法包括使用滑动窗口结合哈希表(unordered_map 和 unordered_set)、双指针配合哈希表以及仅使用滑动窗口。每种方法的时间复杂度均为 O(n),空间复杂度各异。代码示例展示了不同实现方式的细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。

示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子字符串是 “abc”,所以其长度为 3。

示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子字符串是 “b”,所以其长度为 1。

示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

示例 4:
输入: s = “”
输出: 0

提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成

思路1:滑动窗口 + 哈希表

unordered_map

思路如下:从头开始遍历这个字符串,把字符存进哈希表(纪录的是字符出现的次数)。判断该字符出现过几次,如果小于等于 1 次,说明没有重复出现;反之,说明重复出现,就把字符串中该字符第一次出现的位置及之前的子串全部截掉,计算当前不包含重复字符的子字符串的长度。与现有的长度进行比较,更新 连续子字符串 的长度。遍历完成后,最后留下的最长长度即为最终答案。

代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int size = s.size();
        int ret = 0;
        unordered_map<char, int> um;
        int left = 0, right = 0;
        for( ; right < size; ++right){
            um[s[right]]++;
            if(um[s[right]] > 1){ //说明该字符之前出现过
                while(um[s[right]] > 1)
                    um[s[left++]]--;
            }
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};

时间复杂度:O(n);空间复杂度:O(1)。
在这里插入图片描述

unordered_set

也可以用 unordered_set 容器(set容器应该也能用),代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int size = s.size();
        int ret = 0;
        unordered_set<char> us; //出现过的字符存进 us
        int left = 0, right = 0;
        for( ; right < size; ++right){
            if(us.find(s[right]) == us.end()){ //说明没找到重复字符
                ret = max(ret, right - left + 1);
                us.insert(s[right]);
            }
            else{
                while(s[left] != s[right]) //重复出现的字符第一次出现位置之前的子串截掉
                    us.erase(s[left++]);
                left++;
            }
        }
        return ret;
    }
};

在这里插入图片描述

vector(本题中要比使用哈希表更优)

也可以直接使用 vector。使用 vector< int > 时,vector数组的下标就相当于字符转化为整型,由于 s 由英文字母、数字、符号和空格组成,因此 128 大小的数组就够用了。然后该下标对应的 int 值就是该字符出现的次数,同使用哈希表一样。使用vector可以减少运行时间和哈希表占用的内存。

代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int size = s.size();
        int ret = 0;
        vector<int> v(128, 0);
        int left = 0, right = 0;
        for( ; right < size; ++right){
            v[s[right]]++;
            if(v[s[right]] > 1){ //说明该字符之前出现过
                while(v[s[right]] > 1)
                    v[s[left++]]--;
            }
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};

在这里插入图片描述

思路2:双指针 + 哈希表

我们也可以使用哈希表纪录字符出现的位置,当遇到重复字符的时候,可以直接跳到重复字符的后边,继续进行遍历判断。

代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int size = s.size();
        int ret = 0;
        unordered_map<char, int> um; //出现过的字符的索引存进 um
        int left = 0, right = 0;
        for( ; right < size; ++right){
            if(um.find(s[right]) != um.end()){ //说明找到重复字符
                left = max(um[s[right]] + 1, left);
            }
            um[s[right]] = right; //更新重复字符的索引
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};

时间复杂度:O(n);空间复杂度:O(1)。
在这里插入图片描述

思路3:滑动窗口(不用哈希表)

用string来代替哈希表,降低空间复杂度。

代码如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int size = s.size();
        int ret = 0; 
        int cur = 0; //存放当前字符在子串中的索引
        string str = ""; //存放子串
        for(int i = 0 ; i < size; ++i){
            cur = str.find(s[i]);
            if(cur != -1){ //出现过
                ret = ret < str.size() ? str.size() : ret;
                str.erase(0, cur + 1); //去掉重复字符及之前的字符
            }
            str += s[i];
        }
        return ret < str.size() ? str.size() : ret;
    }
};

时间复杂度:O(n);空间复杂度:O(1)。
在这里插入图片描述

好的,关于力扣C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣中,使用 string 可以方便地处理字符串相关的问。 9. 注意边界条件:在力扣中,边界条件往往是解决问的关键。需要仔细分析目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值