文章目录
题目描述
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 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 来检查重复元素。
- 我们定义 left 和 right 两个指针表示一个特定的子字符串的首和尾。初始它们都在索引 0 处。
- 移动 right,并同时向 hashset 中添加 right 所指的元素值,当 right 指到重复的元素时,right - left 的值就是一个不含重复字符的子字符串的长度。
- 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;
}
};
如有帮助到您,可以多多点赞、评论鼓励哟~~~