题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
示例 4:
输入: s = “”
输出: 0
解答1
思路
滑动窗口思想。以字符串abcad
为例。
- 首先,申请一个队列。左指针和右指针同时指向a,a入队。经过两轮循环后,右指针指向c。
字符串:
a b c a d
↑ ↑
l r
队列:abc
- 此时检测下一个字符为a,与前面的a重复了。按照FIFO原理,第一个字符a出队。
字符串:
a b c a d
↑ ↑
l r
队列:abc
- 再检查一遍,此时队中只有bc,不存在与a重复的字符,a可入队。
字符串:
a b c a d
↑ ↑
l r
队列:bca
- 每入队一次,都检查是否需要修改最大子串长度。
需要的头文件
unordered_set。(c++11新模板)
代码
/*
* @lc app=leetcode.cn id=3 lang=cpp
*
* [3] 无重复字符的最长子串
*/
// @lc code=start
#include <algorithm>
#include <string>
#include <unordered_set>
using namespace std;
class Solution
{
public:
int lengthOfLongestSubstring(string s)
{
//异常输入排除
if (s.size() == 0)
{
return 0;
}
else if (s.size() == 1)
{
return 1;
}
// 以哈希表为基础的set
unordered_set<char> subs;
// lp为左指针,rp为右指针,subslength为当前子串长度,maxlength为最大子串长度
int lp = 0, rp = 1, maxlength = 0;
//第一个字符插入子串
subs.insert(s[0]);
while (rp < s.size())
{
//如果找到了之前subs中的字符(重复)
if (subs.find(s[rp]) != subs.end())
{
//擦掉最左边的字符,左指针加1
subs.erase(s[lp]);
lp++;
}
//没有重复的情况
else
{
//当前右指针指向的元素入set
subs.insert(s[rp]);
//右指针+1
rp++;
//确定一次最大长度
maxlength = (subs.size() > maxlength) ? subs.size() : maxlength;
}
}
return maxlength;
}
};
// @lc code=end
时间复杂度和空间复杂度
- 时间复杂度:遍历一次字符串,所以为 O(N) ,其中 N 是字符串的长度。
- 空间复杂度:申请了一个set存放字符,所以为 O(N) ,其中 N 是字符串的长度。
解答2
思路
代码已详细阐述。实际上,仍然是滑动窗口思想。
需要的头文件
vector
代码
/*
* @lc app=leetcode.cn id=3 lang=cpp
*
* [3] 无重复字符的最长子串
*/
// @lc code=start
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
class Solution
{
public:
int lengthOfLongestSubstring(string s)
{
//定义一个vevtor数组,128大小是为了包括所有的大小写字母
vector<int> ascii_vector(128, 0);
//定义最长子串长度变量
int subslength = 0;
//定义左指针,一开始指向位置0
int lp = 0;
//遍历字符串
for (int i = 0; i < s.size(); i++)
{
/*
原理:
ascii_vector:保存了字符串中某个字符最后一次出现时,下一个字符的下标
ascii_vector初始全为0,遍历数组:
如果当前ascii码下标处(ascii_vector[s[i]])为0,证明之前没出现过该字符。
如果当前ascii码下标处不为0,则证明之前已经出现过该字符。
lpcount:如果检测到重复的字符,应返回之前该重复字符最后一次出现的位置的下个字符的下标。
*/
// 当出现重复字符时,ascii_vector一定比左指针大,lp就会指向该重复字符最后一次出现的位置的下个字符的下标。
lp = max(lp, ascii_vector[s[i]]);
// 既然检测到该字符,那么这个字符就是最后一次出现的位置,将下一个字符下标传进来vector里
ascii_vector[s[i]] = i + 1;
//i相当于右指针,lp相当于左指针,subslength每次都会记录最长子串长度
subslength = max(subslength, i - lp + 1);
}
return subslength;
}
};
// @lc code=end
时间复杂度和空间复杂度
- 时间复杂度:左指针和右指针分别会遍历整个字符串一次,所以为 O(N) ,其中 N 是字符串的长度。
- 空间复杂度:在本题中没有明确说明字符集,因此可以默认为所有 ASCII 码在0~128内的字符。所以为 O(X) ,其中X为字符集中字符总个数。
反思与总结
- 灵活利用vector数组+ascii编码的特点进行解题。这里就是运用了ascii码实际上可以充当数字下标的特性。
- 灵活建立指针帮助解题。这里自行建立了左指针,然后把循环的i当作右指针,方便计算出子串的长度。