您的位置:首页 > 其它

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

2020-03-08 11:38 225 查看

题目

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

示例 1:

输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

解法1 暴力遍历

暴力遍历即对字符串中n(n+1)2\frac{n(n+1)}{2}2n(n+1)​个子字符串挨个进行验证。设置i作为字符串开始的索引,设置j作为字符尾部索引,则有i:0→(len−1),j:i→(len−1)i:0\to(len -1),j:i \to (len-1)i:0→(len−1),j:i→(len−1),此外每当j+1时候都要通过遍历当前子字符串进行判断是否有重复,所以一共是3层循环。

时间复杂度 O(n3)O(n^3)O(n3)
空间复杂度 建立的子字符集大小,最长为n - 1, O(n)O(n)O(n)

效率太慢 100%TLE

解法2 滑动窗口

在暴力遍历中,对于iii开始的的字符集,我们需要O(n2)O(n^2)O(n2)的时间复杂度来验证是否存在重复字符。
对于这一部分我们可以考虑利用hashset来优化。

滑动窗口是数组/字符串问题中常用的抽象概念。 窗口通常是在数组/字符串中由开始和结束索引定义的一系列元素的集合,即 [i, j)[i,j)(左闭,右开)。而滑动窗口是可以将两个边界向某一方向“滑动”的窗口。例如,我们将 [i, j)[i,j) 向右滑动 11 个元素,则它将变为 [i+1, j+1)[i+1,j+1)(左闭,右开)。

对于[i,j)来说(起初i = j + 1),我们判断当前hashset中是否存在该元素,若不存在,则j继续往右移动并更新最大窗口长度,若存在,则从hashset中移除i对应的元素,然后i向右移动,然后重新进行判断。

class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> char_set;
typedef pair<char,int> Int_Par;
int len = 0, left = 0,right = 0;
while(right < s.size())
{
if(char_set.find(s[right]) == char_set.end())
{
char_set.insert(Int_Par(s[right],right));
right = right + 1;
len = max(len,right - left);
}
else{
char_set.erase(s[left]);
left = left + 1;
}
}
return len;
}
};

时间复杂度 O(2n)→O(n)O(2n) \to O(n)O(2n)→O(n) 最坏情况下i和j都会遍历完数组
空间复杂度 建立的字符hashset长度,最大长度为n。O(min(m,n))O(min(m,n))O(min(m,n))

滑动窗口优化

我们可以将两次遍历优化成一次遍历
修改hashset的结构为 key:s[i] ->value:i。这样当发现元素与已有hashset元素重复时,则可以查到到当前hashset的重复元素对应的数组索引index。然后将i修改成index + 1;

class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.length();
unordered_map<char,int> char_set;
if(len <= 1)
return len;
int start = 0,end = 0,maxlen = 0;
while(start < len && end < len)
{

auto hm = char_set.find(s[end]);
if(hm != char_set.end())
{
start = max(hm->second + 1,start); //start进行跃迁时,可能会跳过一些元素,而这些元素还在hash集合里面无法处理。所以当搜索的时候我们要判断元素是否在滑动窗口内。
char_set.erase(hm);
}

char_set.insert(make_pair(s[end],end)); // c++ hashset的insert插入相同key时候无法覆盖value,所以必须先移除再插入。来保证value永远是最靠右元素的索引
end++;
maxlen = max(maxlen,end - start);
}
return maxlen;

}
};

时间复杂度 O(n)O(n)O(n)
空间复杂度 O(min(m,n))O(min(m,n))O(min(m,n)) m为字符集长度,最大不超过n.

  • 点赞
  • 收藏
  • 分享
  • 文章举报
火与剑 发布了41 篇原创文章 · 获赞 0 · 访问量 281 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: