[剑指Offer]:最长不含重复字符的子字符串

126 阅读2分钟

文章目录


题目描述

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

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

题解思路

方法一:滑动窗口

使用滑动窗口解决的,滑动窗口为 [left,i],每遍历一个字符都使用 v 数组更新字符出现的位置,确保每个滑动窗口都只包含不同的字符。为了达到这一目的,只需要判断上一次出现的该字符是否在窗口内,如果在的话就将窗口移动到 v[s[i]] 的后一个位置,如果不在的话直接将 i 添加到窗口后面即可。

代码实现:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int left = 0;
        int n = s.size();
        int res = 0;
        vector<int> v(128, -1);
        for(int i = 0; i < n; ++i){
            if(v[s[i]] >= left){
                left = v[s[i]]+1;
            }
            v[s[i]] = i;
            res = max(res, i-left+1);
        }
        return res;
    }
};

方法二:优化的滑动窗口(哈希表)

使用哈希表记录每个字符的下一个索引,然后尽量向右移动尾指针来拓展窗口,并更新窗口的最大长度。如果尾指针指向的元素重复,则将头指针直接移动到窗口中重复元素的右侧。

  • tail 指针向末尾方向移动;

  • 如果尾指针指向的元素存在于哈希表中:

    • head 指针跳跃到重复字符的下一位;
  • 更新哈希表和窗口长度。

复杂度分析

  • 时间复杂度:O(n),遍历了一遍 s,哈希表中查找的时间复杂度为 O(1)。
  • 空间复杂度:O(n),使用了哈希表。

代码实现:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        map<char, int> m;
        int res = 0, l = 0, r = 0;
        while (r < s.size()) {
            if (m.find(s[r]) != m.end()) {
                l = max(l, m[s[r]] + 1);
            }
            m[s[r++]] = r;
            res = max(r - l, res);
        }
        return res;
    }
};

方法三:哈希表

利用 hashset 来检查重复元素。

  1. 我们定义 left 和 right 两个指针表示一个特定的子字符串的首和尾。初始它们都在索引 0 处。
  2. 移动 right,并同时向 hashset 中添加 right 所指的元素值,当 right 指到重复的元素时,right - left 的值就是一个不含重复字符的子字符串的长度。
  3. erase 掉 haseset 中的 left 所指元素的值,然后让 left++(for 循环的作用)。之后继续移动 right 来找到下一个不含重复字符的子字符串的长度。

代码实现:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.length();
        if(len < 2) return len;

        int res = -1, right = 0;
        unordered_set<char> store;
        for(int left = 0; left < len; ++left) {
            while(right < len && !store.count(s[right])) {
                store.insert(s[right]);
                ++right;
            }
            res = max(res, right - left);
            store.erase(s[left]);
            if(right >= len) break;
        }

        return res;
    }
};

如有帮助到您,可以多多点赞、评论鼓励哟~~~