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;
    }
};

### LeetCode Hot 100 Problems C++ Solutions 以下是针对LeetCode热门题目的一些常见问题及其C++解决方案的概述。这些解答基于常见的算法思想,如滑动窗口、哈希表以及字符操作等。 #### 反转字符 对于反转字符的问题,可以采用双指针的方法来交换数组中的字符位置[^1]。这种方法的时间复杂度为O(n),其中n是字符长度,而空间复杂度为O(1)。 ```cpp class Solution { public: void reverseString(std::vector<char>& s) { int n = s.size(); int left = 0, right = n - 1; while (left < right) { std::swap(s[left++], s[right--]); } } }; ``` #### 不重复最长子串 不重复最长子串可以通过滑动窗口的思想实现[^2]。通过维护一个动态变化的窗口并记录当前无重复字符大长度,可以在一次遍历中完成计算。 ```cpp class Solution { public: int lengthOfLongestSubstring(const std::string& s) { std::unordered_set<char> window; int maxLength = 0, start = 0; for(int i = 0;i<s.length();i++) { char currentChar = s[i]; while(window.find(currentChar)!=window.end()) { window.erase(s[start]); ++start; } window.insert(currentChar); maxLength = std::max(maxLength,i-start+1); } return maxLength; } }; ``` #### 多数元素查找 多数元素查找问题是另一个经典案例,通常可以用摩尔投票法或者哈希映射方法解决[^3]。这里展示的是利用哈希表的方式,其时间复杂度为O(n),空间复杂度同样为O(n)。 ```cpp class Solution { public: int majorityElement(std::vector<int>& nums) { std::map<int,int> countMap; int threshold = nums.size() / 2; for(auto num : nums){ if(countMap.find(num)==countMap.end()){ countMap[num]=1; }else{ countMap[num]++; } if(countMap[num]>threshold){ return num; } } return -1; } }; ``` #### 广度优先搜索(BFS) 广度优先搜索是一种用于图结构探索的技术,在某些场景下也可以应用于语义关联网络等问题[^4]。下面是一个简单的BFS模板例子: ```cpp #include <queue> #include <vector> void bfs(int root,std::vector<std::vector<int>> adjList){ std::queue<int> q; std::vector<bool> visited(adjList.size(),false); q.push(root); visited[root]=true; while(!q.empty()){ int node=q.front();q.pop(); // Process the node here... for(auto neighbor:adjList[node]){ if(!visited[neighbor]){ q.push(neighbor); visited[neighbor]=true; } } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值