一、题目描述
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2:
输入: “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。 示例 3:
输入: “pwwkew” 输出: 3 解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
二、题解
1.暴力求解(利用的散列的思想)
散列(hash):解决A中元素是否在B中出现过的问题。
简化后的问题直观理解:给定N个整数,在给出M个整数,问这M个数中的每个数分别是否在N个数中出现过?(例如N=5时,n个整数[8,3,7,6,2];M=3时,m个整数[7,6,2])
直观思路:对M个数中每个欲查询的数x,遍历所有N个数中,看是否有与x相等的数。时间复杂度O(MN)。当M和N数量级非常大时,这种算法时间太慢。
解决办法:利用空间换时间。设定bool型数组hashtable[100010],hashtable[x]=true,表示x存在于N个整数中,false代表不存在。这样只需要遍历N个整数一次,对hashtable进行预处理。然后遍历M中的整数x,直接访问数组就可以知道x是否在N中出现过。
以上引用自《算法笔记》。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.size();
if(len==0||len==1) return len;
int maxlen = 0;
int left = 0,right=left;
map<char,int> mp;
while(right<len){
int cnt = 0;
char c= s[right];
if(mp[c]==0) {
++mp[c];
++right;
}
else{
int temp = right - left;
if(maxlen<temp) maxlen = temp;
mp.clear();
++left;
right = left;
}
}
return maxlen>(right-left)?maxlen:(right-left);
}
};
2.暴力求解优化
在暴力求解1中,每次遇到相同字符移动左端点时都要重置map,消耗了大量时间。所以采用了cnt代表我们遍历字符串的次数,利用map[‘c’]的int存放被统计过的次数,map[‘c’]<cnt表示在新一轮遍历中还没被统计过,所以将它置为cnt。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.size();
if(len==0||len==1) return len;
int maxlen = 0;
int left = 0,right=left;
int cnt = 1; //统计次数
map<char,int> mp;
while(right<len){
char c= s[right];
if(mp[c]<cnt) {
mp[c]=cnt;
++right;
}
else{
int temp = right - left;
if(maxlen<temp) maxlen = temp;
++cnt;
++left;
right = left;
}
}
return maxlen>(right-left)?maxlen:(right-left);
}
};
3.滑动窗口(双指针)
在暴力法中,我们会反复检查一个子字符串是否含有有重复的字符。在查找区间为[left,right-1]若找到s[pos]==s[right],从left到pos位置开始的无重复子串长度,都会小于right-left(因为一定会遇到right位),所以下一轮查找直接从pos+1开始就可以了。且[left,right-1]区间是没有相同字符的,所以[pos+1,right-1]也是没有相同字符的,且right与[pos+1,right-1]的字符一定都不相同(若相同,在原区间就与与pos相同的字符了,这不符合假设。),所以新的查找区间为[pos+1,right+1]。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.size();
if(len==0||len==1) return len;
int maxlen = 0;
int left = 0,right=1;
while(right<len){
int temp = s.find(s[right],left);//从left开始查找是都出现s[right]
if(temp<right){
maxlen = maxlen>(right - left)?maxlen:(right-left);
left = temp + 1;
}
++right;
}
return maxlen>(right - left)?maxlen:(right-left);
}
};
三、滑动窗口相关题目
3.无重复字符的最长子串
30.串联所有单词的子串
76.最小覆盖子串
159.至多包含两个不同字符的最长子串
209.长度最小的子数组
239.滑动窗口最大值
567.字符串的排列
632.最小区间
727.最小窗口子序列