leetcode 链接:无重复字符的最长字串
题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例:
输入: s = "abcabcbb"输出: 3解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
输入: s = ""
输出: 0
输入: s = " "
输出: 1
输入: s = "abba"
输出: 2
输入: s = "dvdf"
输出: 3
解答 & 代码
解法一:哈希map+滑动窗口
可用字符串 "dvdf" 过一遍流程
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 设置一个哈希表存储字符串 s 中出现过的字符及其最后出现的位置下标
unordered_map<char, int> chaPosMap;
int maxLen = 0; // 无重复字符的最长子串长度
int subStart = 0; // 当前无重复字符子串的起始下标
// 遍历字符串
for(int i = 0; i < s.length(); ++i)
{
// 如果当前字符在哈希表中存在且位置下标大于等于 subStart,说明当前的子串出现重复字符了
if(chaPosMap.find(s[i]) != chaPosMap.end() && chaPosMap[s[i]] >= subStart)
{
maxLen = (i - subStart) > maxLen ? (i - subStart) : maxLen; // 更新最长子串长度
subStart = chaPosMap[s[i]] + 1; // 将子串起点移到重复字符之后
chaPosMap[s[i]] = i; // 将哈希表中当前字符对应的下标更新为 i
}
// 如果当前字符在哈希表中不存在
else if(chaPosMap.find(s[i]) == chaPosMap.end())
{
chaPosMap.insert(make_pair(s[i], i)); // 将当前字符及其下标插入到哈希表
}
// 如果当前字符在哈下表中存在,但其位置下标小于等于 subStart,说明并不是重复字符,更新哈希表中的下标
else
{
chaPosMap[s[i]] = i;
}
}
// 注意最后更新一下 maxLen,否则如果字符串例如 " ",字符串遍历结束但未更新 maxLen
maxLen = (s.length() - subStart) > maxLen ? (s.length() - subStart) : maxLen;
return maxLen;
}
};
执行结果:
执行结果:通过
执行用时:20 ms, 在所有 C++ 提交中击败了 68.13% 的用户
内存消耗:8.1 MB, 在所有 C++ 提交中击败了 66.41% 的用户
换一种写法:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int sLen = s.size();
if(sLen <= 1)
return sLen;
int maxSubLen = 0;
unordered_map<char, int> winMap; // 哈希表,存储窗口中的字符及其出现的下标
int left = 0, right = 0; // 窗口的左右边界
while(left < sLen || right < sLen)
{
// 如果右边界未越界,并且右边界的字符在窗口中不存在(哈希表中找不到,或找到了下标小于左边界)
// 则窗口右边界右移
while(right < sLen &&
(winMap.find(s[right]) == winMap.end() || winMap[s[right]] < left))
{
winMap[s[right]] = right;
++right;
}
// 更新最长子串的长度
if(right - left > maxSubLen)
maxSubLen = right - left;
if(right >= sLen) // 若右边界越界,则停止滑动窗口,结束
break;
else // 左边界跳到重复元素之后
left = winMap[s[right]] + 1;
}
return maxSubLen;
}
};
执行结果:通过
执行用时:16 ms, 在所有 C++ 提交中击败了 75.14% 的用户
内存消耗:8.2 MB, 在所有 C++ 提交中击败了 63.97% 的用户
解法二:哈希set+滑动窗口
上面的做法用空间换时间,哈希表中记录了字符的下标,因此当窗口右边界为重复值时,可从哈希表中查找到窗口中该重复值的位置,左边界直接跳到该位置后一个位置
也可以哈希表中只存字符,而不存键值对:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int sLen = s.size();
if(sLen <= 1)
return sLen;
int maxSubLen = 0;
unordered_set<char> winMap; // 哈希表,存储窗口中出现过的字符
winMap.insert(s[0]);
int left = 0, right = 1; // 左、右边界
while(left < sLen)
{
// 右边界右移
while(right < sLen && winMap.find(s[right]) == winMap.end())
{
winMap.insert(s[right]);
++right;
}
// 更新最长子串长度
if(right - left > maxSubLen)
maxSubLen = right - left;
// 左边界右移
winMap.erase(s[left]);
++left;
}
return maxSubLen;
}
};
执行结果:
执行结果:通过
执行用时:28 ms, 在所有 C++ 提交中击败了 53.21% 的用户
内存消耗:10 MB, 在所有 C++ 提交中击败了 17.08% 的用户
