3.无重复字符的最长子串

题目描述

给定一个字符串 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 由英文字母、数字、符号和空格组成

方法一:滑动窗口(双指针),控制右指针

int lengthOfLongestSubstring(string s)
{
    unordered_set<char> hashset;
    hashset.insert(s[0]);
    unsigned maxLength = 1;
    unsigned int left = 0; // 用来标识当前无重复字符串的起始位置

    for (unsigned right = 1; right < s.size(); right++)
    {
        if (hashset.find(s[right]) == hashset.end()) // hashset 不存在当前字符
        {
            hashset.insert(s[right]);
            maxLength = max(maxLength, right - left + 1);
        }
        else
        {
            if (hashset.size() == 1) // hashset 中只有一个字符,且和下一个重复
            {
                left++;
                continue;
            }
            hashset.erase(s[left++]);  // 滑动窗口左侧删除一个元素,并将左侧前进1
            while (left < right && hashset.find(s[right]) != hashset.end())  // 重复字符还未删除
                hashset.erase(s[left++]); // 去除无重复字符串(滑动窗口)最左侧字符
            
            hashset.insert(s[right]);  // 窗口内重复元素已经删除,将新元素插入窗口
        }
    }
    return maxLength;
}

方法二:滑动窗口(双指针),控制左指针

int lengthOfLongestSubstring2(string s)
{
    unordered_set<char> hashset;
    int right = -1;   // 相当于滑动窗口右边边界。初始时滑动窗口还不存在,为-1。
    int maxLength = 0;
    for (int left = 0; left < s.size(); left++)
    {
        if (left != 0)
            hashset.erase(s[left - 1]);   // 滑动窗口左侧向右移动一个位置
        // 只有当滑动窗口下一个元素不在滑动窗口内时,才加入滑动窗口,并且滑动窗口右侧扩1
        while (right + 1 < s.size() && !hashset.count(s[right + 1]))
        {
            hashset.insert(s[right + 1]);
            right++;
        }
        maxLength = max(maxLength, right - left + 1);
    }
    return maxLength;
}

相比控制右指针,控制左指针性能相对会低一点点