力扣刷题日记16---剑指 Offer II 016. 不含重复字符的最长子字符串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子字符串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子字符串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
示例 4:
输入: s = “”
输出: 0
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
思路1:滑动窗口 + 哈希表
unordered_map
思路如下:从头开始遍历这个字符串,把字符存进哈希表(纪录的是字符出现的次数)。判断该字符出现过几次,如果小于等于 1 次,说明没有重复出现;反之,说明重复出现,就把字符串中该字符第一次出现的位置及之前的子串全部截掉,计算当前不包含重复字符的子字符串的长度。与现有的长度进行比较,更新 连续子字符串 的长度。遍历完成后,最后留下的最长长度即为最终答案。
代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int size = s.size();
int ret = 0;
unordered_map<char, int> um;
int left = 0, right = 0;
for( ; right < size; ++right){
um[s[right]]++;
if(um[s[right]] > 1){ //说明该字符之前出现过
while(um[s[right]] > 1)
um[s[left++]]--;
}
ret = max(ret, right - left + 1);
}
return ret;
}
};
时间复杂度:O(n);空间复杂度:O(1)。
unordered_set
也可以用 unordered_set 容器(set容器应该也能用),代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int size = s.size();
int ret = 0;
unordered_set<char> us; //出现过的字符存进 us
int left = 0, right = 0;
for( ; right < size; ++right){
if(us.find(s[right]) == us.end()){ //说明没找到重复字符
ret = max(ret, right - left + 1);
us.insert(s[right]);
}
else{
while(s[left] != s[right]) //重复出现的字符第一次出现位置之前的子串截掉
us.erase(s[left++]);
left++;
}
}
return ret;
}
};
vector(本题中要比使用哈希表更优)
也可以直接使用 vector。使用 vector< int > 时,vector数组的下标就相当于字符转化为整型,由于 s 由英文字母、数字、符号和空格组成,因此 128 大小的数组就够用了。然后该下标对应的 int 值就是该字符出现的次数,同使用哈希表一样。使用vector可以减少运行时间和哈希表占用的内存。
代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int size = s.size();
int ret = 0;
vector<int> v(128, 0);
int left = 0, right = 0;
for( ; right < size; ++right){
v[s[right]]++;
if(v[s[right]] > 1){ //说明该字符之前出现过
while(v[s[right]] > 1)
v[s[left++]]--;
}
ret = max(ret, right - left + 1);
}
return ret;
}
};
思路2:双指针 + 哈希表
我们也可以使用哈希表纪录字符出现的位置,当遇到重复字符的时候,可以直接跳到重复字符的后边,继续进行遍历判断。
代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int size = s.size();
int ret = 0;
unordered_map<char, int> um; //出现过的字符的索引存进 um
int left = 0, right = 0;
for( ; right < size; ++right){
if(um.find(s[right]) != um.end()){ //说明找到重复字符
left = max(um[s[right]] + 1, left);
}
um[s[right]] = right; //更新重复字符的索引
ret = max(ret, right - left + 1);
}
return ret;
}
};
时间复杂度:O(n);空间复杂度:O(1)。
思路3:滑动窗口(不用哈希表)
用string来代替哈希表,降低空间复杂度。
代码如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int size = s.size();
int ret = 0;
int cur = 0; //存放当前字符在子串中的索引
string str = ""; //存放子串
for(int i = 0 ; i < size; ++i){
cur = str.find(s[i]);
if(cur != -1){ //出现过
ret = ret < str.size() ? str.size() : ret;
str.erase(0, cur + 1); //去掉重复字符及之前的字符
}
str += s[i];
}
return ret < str.size() ? str.size() : ret;
}
};
时间复杂度:O(n);空间复杂度:O(1)。