原题链接
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
解题思路
本题可用滑动窗口(双指针),枚举 i i i 为以 i i i 为结尾的子串,找出最长的无重复字符的子串,也就等同于,找出最左的 j j j。
为了判定 [ j , i ] [j, i] [j,i] 之间是否有重复字符,可以使用哈希表维护 [ j , i ] [j, i] [j,i] 中每个字符出现的次数。
当然,如果 j j j 每次都从 i i i 遍历到0,那么整体的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。双指针算法的优化首先考虑其单调性,即 i i i 增加1后,新的 j ′ j' j′ 是否会出现在 j j j 之前。
用反证法即可证明。如上图所示,
i
i
i 增加1变为
i
′
i'
i′ 后,如果
j
′
′
j''
j′′ 满足题意,使
[
j
′
′
,
i
′
]
[j'', i']
[j′′,i′] 为最长的子串,且之间无重复字符,那么可推
[
j
′
′
,
i
]
[j'', i]
[j′′,i] 之间也满足之间无重复字符,这与
[
j
,
i
]
[j, i]
[j,i] 为最长的子串相悖。故可证之,
i
i
i 增加1变为
i
′
i'
i′ 后,
j
′
j'
j′ 只能从
j
j
j 开始枚举。
这就意味着, j j j 一旦开始枚举后,可以不回头一直往前走,这样整体复杂度就降为 O ( n ) O(n) O(n)。
在实际实现中, i + 1 i+1 i+1 时将对应的值 S i + 1 S_{i+1} Si+1 移入表中,如果表中有重复,则意味着 S i + 1 S_{i + 1} Si+1 元素重复,将 S j S_j Sj 移除表,并执行 j = j + 1 j = j + 1 j=j+1 ,直至表中无重复的 S i + 1 S_{i+1} Si+1 为止。
AC代码
滑动窗口 O ( n ) O(n) O(n):
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash;
int res = 0;
for (int i = 0, j = 0; i < s.size(); i++)
{
hash[s[i]]++;
while (hash[s[i]] > 1) hash[s[j++]]--;
res = max(res, i - j + 1);
}
return res;
}
};