哈希算法题目:无重复字符的最长子串
字数 2423 2025-12-12 08:35:54

哈希算法题目:无重复字符的最长子串


题目描述

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

  1. 输入 s = "abcabcbb",最长无重复字符子串是 "abc",长度为 3。
  2. 输入 s = "bbbbb",最长子串是 "b",长度为 1。
  3. 输入 s = "pwwkew",最长子串是 "wke",长度为 3(注意 "pwke" 不是子串,而是子序列,子串必须连续)。

要求

  • 时间复杂度尽可能低,最好在 O(n) 内完成。
  • 用哈希表(或数组映射)记录字符出现的最新位置,以快速判断重复字符并更新子串起始点。

解题步骤详解

第一步:问题理解

我们要找一个连续的、内部不包含重复字符的子串,并计算它的最大长度。
关键难点

  • 当遇到重复字符时,不能简单地将当前子串全部舍弃,而是要将子串起点更新到“上一次重复字符的下一个位置”,以确保新子串中没有重复字符。
  • 需要一个快速的方法知道重复字符上一次出现的位置,这自然想到用哈希表。

第二步:思路设计

  1. 用两个指针(索引)leftright 来表示当前无重复字符子串的边界:
    • left 指向当前子串的开始位置。
    • right 是遍历字符串的索引,指向当前检查的字符。
  2. 用一个哈希表(比如 unordered_map<char, int> 或长度为 128 的数组,适用于 ASCII 字符)来记录每个字符最后一次出现的位置
  3. 遍历字符串,对每个 s[right]
    • 如果这个字符之前出现过,并且它的上一次出现位置在 left 及其右侧(说明在当前子串内重复了),那么将 left 更新为上一次出现位置的下一个索引,从而“跳过”重复字符,保证新子串不含重复。
    • 不管是否重复,更新这个字符在哈希表中的最新位置为 right
    • 计算当前子串长度 right - left + 1,并更新最大长度。

第三步:具体例子推演

s = "abcabcbb" 为例:

right s[right] 上一次出现位置 left 变化 当前子串 长度 最大长度
0 a -1 left=0 a 1 1
1 b -1 left=0 ab 2 2
2 c -1 left=0 abc 3 3
3 a 0 0 ≥ left? 是 → left=0+1=1 bca 3 3
4 b 1 1 ≥ left=1? 是 → left=1+1=2 cab 3 3
5 c 2 2 ≥ left=2? 是 → left=2+1=3 abc 3 3
6 b 4 4 ≥ left=3? 是 → left=4+1=5 cb 2 3
7 b 6 6 ≥ left=5? 是 → left=6+1=7 b 1 3

最终结果为 3。


第四步:算法实现细节

int lengthOfLongestSubstring(string s) {
    // 哈希表,key 是字符,value 是该字符最后一次出现的索引
    unordered_map<char, int> charIndexMap;
    int maxLen = 0;
    int left = 0;  // 当前无重复子串的起点
    
    for (int right = 0; right < s.size(); ++right) {
        char ch = s[right];
        // 如果这个字符之前出现过,并且上一次出现的位置在当前子串范围内(即 >= left)
        if (charIndexMap.find(ch) != charIndexMap.end() && charIndexMap[ch] >= left) {
            // 将 left 移到上一次出现位置的下一个位置
            left = charIndexMap[ch] + 1;
        }
        // 更新字符的最新位置
        charIndexMap[ch] = right;
        // 计算当前子串长度
        int curLen = right - left + 1;
        maxLen = max(maxLen, curLen);
    }
    return maxLen;
}

注意

  • 判断重复时不仅要检查字符是否在哈希表中存在,还要看其上一次出现的位置是否在当前子串内(charIndexMap[ch] >= left),因为在 left 之前的重复字符已经被“抛弃”了,不影响当前子串。
  • 由于字符集有限(比如 ASCII 128),也可以用数组 int lastPos[128] 并初始化为 -1,这样更高效。

第五步:复杂度分析

  • 时间复杂度 O(n):只需要遍历一次字符串,哈希表的插入和查找是 O(1)。
  • 空间复杂度 O(字符集大小):哈希表存储每个字符最后一次出现的位置,最多 128 或 256 个条目(对 ASCII 或扩展 ASCII)。

第六步:思考扩展

  1. 如果字符集很大(比如 Unicode),哈希表方案依然适用,因为字符种类有限,不会无限增长。
  2. 类似问题变形:找出最长子串本身(而不仅仅是长度)——可以在更新最大长度时记录起止位置,最后截取子串。
  3. 如果允许最多重复 k 次(而不是 0 次),可以改为用哈希表记录字符出现次数,结合滑动窗口。

通过以上步骤,你可以理解如何利用哈希表记录字符位置,并配合双指针在 O(n) 时间内高效地找到无重复字符的最长子串长度。

哈希算法题目:无重复字符的最长子串 题目描述 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 例如: 输入 s = "abcabcbb" ,最长无重复字符子串是 "abc" ,长度为 3。 输入 s = "bbbbb" ,最长子串是 "b" ,长度为 1。 输入 s = "pwwkew" ,最长子串是 "wke" ,长度为 3(注意 "pwke" 不是子串,而是子序列,子串必须连续)。 要求 : 时间复杂度尽可能低,最好在 O(n) 内完成。 用哈希表(或数组映射)记录字符出现的最新位置,以快速判断重复字符并更新子串起始点。 解题步骤详解 第一步:问题理解 我们要找一个连续的、内部不包含重复字符的子串,并计算它的最大长度。 关键难点 : 当遇到重复字符时,不能简单地将当前子串全部舍弃,而是要将子串起点更新到“上一次重复字符的下一个位置”,以确保新子串中没有重复字符。 需要一个快速的方法知道重复字符上一次出现的位置,这自然想到用哈希表。 第二步:思路设计 用两个指针(索引) left 和 right 来表示当前无重复字符子串的边界: left 指向当前子串的开始位置。 right 是遍历字符串的索引,指向当前检查的字符。 用一个哈希表(比如 unordered_map<char, int> 或长度为 128 的数组,适用于 ASCII 字符)来记录 每个字符最后一次出现的位置 。 遍历字符串,对每个 s[right] : 如果这个字符之前出现过,并且它的上一次出现位置在 left 及其右侧(说明在当前子串内重复了),那么将 left 更新为上一次出现位置的下一个索引,从而“跳过”重复字符,保证新子串不含重复。 不管是否重复,更新这个字符在哈希表中的最新位置为 right 。 计算当前子串长度 right - left + 1 ,并更新最大长度。 第三步:具体例子推演 以 s = "abcabcbb" 为例: | right | s[ right ] | 上一次出现位置 | left 变化 | 当前子串 | 长度 | 最大长度 | |-------|----------|----------------|-------------------------------|----------------|-------|----------| | 0 | a | -1 | left=0 | a | 1 | 1 | | 1 | b | -1 | left=0 | ab | 2 | 2 | | 2 | c | -1 | left=0 | abc | 3 | 3 | | 3 | a | 0 | 0 ≥ left? 是 → left=0+1=1 | bca | 3 | 3 | | 4 | b | 1 | 1 ≥ left=1? 是 → left=1+1=2 | cab | 3 | 3 | | 5 | c | 2 | 2 ≥ left=2? 是 → left=2+1=3 | abc | 3 | 3 | | 6 | b | 4 | 4 ≥ left=3? 是 → left=4+1=5 | cb | 2 | 3 | | 7 | b | 6 | 6 ≥ left=5? 是 → left=6+1=7 | b | 1 | 3 | 最终结果为 3。 第四步:算法实现细节 注意 : 判断重复时不仅要检查字符是否在哈希表中存在,还要看其上一次出现的位置是否在当前子串内( charIndexMap[ch] >= left ),因为在 left 之前的重复字符已经被“抛弃”了,不影响当前子串。 由于字符集有限(比如 ASCII 128),也可以用数组 int lastPos[128] 并初始化为 -1,这样更高效。 第五步:复杂度分析 时间复杂度 O(n):只需要遍历一次字符串,哈希表的插入和查找是 O(1)。 空间复杂度 O(字符集大小):哈希表存储每个字符最后一次出现的位置,最多 128 或 256 个条目(对 ASCII 或扩展 ASCII)。 第六步:思考扩展 如果字符集很大(比如 Unicode),哈希表方案依然适用,因为字符种类有限,不会无限增长。 类似问题变形:找出最长子串本身(而不仅仅是长度)——可以在更新最大长度时记录起止位置,最后截取子串。 如果允许最多重复 k 次(而不是 0 次),可以改为用哈希表记录字符出现次数,结合滑动窗口。 通过以上步骤,你可以理解如何利用哈希表记录字符位置,并配合双指针在 O(n) 时间内高效地找到无重复字符的最长子串长度。