leetcode之hot100---无重复字符的最长子串(C++)

题目理解:

子串是指一个字符串中连续的一部分字符序列。

特点

  1. 连续性:子串必须由相邻的字符组成,不能跳过任何字符。

        例如,在字符串 "abcde" 中:

        子串"abc""bcd""e"

        不是子串"ace"(因为字符不是连续的)。

  1. 包含关系:子串一定是字符串的一部分,但字符串本身也是自己的子串。

  2. 位置无关:子串可以从任意位置开始,长度可以为 1(单个字符)到字符串本身的长度。

任意字符串都一定有子串,因为子串可以是整个字符串本身,也可以是它的一部分,就算是空字符串也算一个子串。所以,只要对字符串进行遍历,去掉重复的字符,就能找到字符串的子串。

 思路一(暴力解法):

  • 遍历字符串的每个字符。
  • 每次遇到一个字符时,检查它前面的部分(它左边的字符)有没有重复。
  • 如果没有重复,就继续往后看,并记录当前最长的连续不重复的字符长度。
  • 如果发现有重复字符,就从重复字符的下一个位置重新开始计算新的子串。
  • 最后,把找到的最长长度返回
class Solution {
public:
    int lengthOfLongestSubstring(string s) {

        // 如果字符串为空,直接返回 0
        if (s.empty()) {
            return 0;
        }

        // 初始化变量
        int maxLen = 0; // 记录最长无重复子串的长度
        int len = 0;    // 当前子串长度

        // 遍历字符串
        for (int i = 0; i < s.size(); ++i) {
            // 检查当前字符在当前子串中是否重复
            for (int j = i - len; j < i; ++j) { // 遍历当前子串的范围
                if (s[j] == s[i]) {  // 如果字符重复
                    len = i - j - 1; // 当前子串长度更新为从重复字符后一位开始
                    break;
                }
            }
            // 更新当前子串长度
            len++;

            // 更新最大长度
            maxLen = max(maxLen, len);
        }

        return maxLen; // 返回最长子串的长度       
    }
};

但是该方法的时间复杂度高达O(N²) ,下面是优化后的算法

 思路二 (滑动窗口法):

  • 通过动态调整 左指针右指针 的位置,维护窗口的范围。
  • 窗口中的状态通过 unordered_set 实时更新,保证窗口内的字符无重复。
  • 左指针负责收缩窗口(移除字符),右指针负责扩展窗口(加入字符)。
  • 每次调整窗口后,更新最长子串的长度。
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //设置一个只包含值的哈希表,用于记录滑动窗口中的字符
        unordered_set<char> occur;
        int n = s.size();
        //设置滑动窗口的右边界索引
        //初始值设为 -1,相当于右边界在字符串的左侧,尚未进入字符串
        int right = -1;
        int lens = 0;
        //遍历字符串的每个字符,将其暂时作为滑动窗口的左边界遍历字符串
        for(int i = 0; i < n; i++){
            //收缩滑动窗口左边界
            if(i != 0){
                occur.erase(s[i - 1]);
            }
            //移动右指针,扩充滑动窗口
            while(right + 1 < n && !occur.count(s[right +1])){
                occur.insert(s[right + 1]);
                ++right;
            }
            lens = max(lens, right - i + 1);

        }
        return lens;
        
    }
};

该方法的时间复杂度高达O(N) ,是解决该问题的最优方法

在复现代码的过程中发现了更符合正常思路的代码,同样使用滑动窗口方法,现更新如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //从字符串的第一个字符开始遍历字符串,借助一个哈希表暂存滑动窗口内的字符
        //如果待遍历的字符已存在于哈希表中,更新滑动窗口左边界以及内部元素
        //否则就将该字符加入哈希表中
        unordered_set<char> window;
        int len = 0;
        int right = 0;
        for(int i = 0; i < s.size(); i++){
            //扩充滑动窗口,如果不含有重复字符,就将其加入滑动窗口内部
            while(right < s.size() && !window.count(s[right])){
                window.insert(s[right]);
                right++;
            }
            
            //更新滑动窗口内部元素
            window.erase(s[i]);
            //更新最大长度
            len = max(len, right - i);                                    
        }
        return len;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值