一、问题描述
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
二、初始思路
从前往后扫描字符串,依次将扫描到的字符加入到一个哈希表中,Key为字符,Value为在字符串中的索引,并用一个indexOfFirst记录当前最大长度无重复串(简称当前串)的初始位置。不断扫描字符串,当遇到重复字符时,若当前串长度大于记录的最长长度,更新最长长度,并将indexOfFirst逐步移向哈希表中记录的重复字符所在位置的下一个索引,在此过程中在哈希表删除路径上的字符,最后更新重复字符的位置,继续重复以上过程,直到全部扫描完毕。
三、初始代码
class Solution {
public int lengthOfLongestSubstring(String s) {
int maxLen=0;
int len=0;
int index=0;
int indexOfFirst=0;//正在扫描字符串的第一个字符的位置
Map<Character,Integer> map=new HashMap<>();
while(index<s.length()){
char c=s.charAt(index);
if(map.get(c)==null){
map.put(c,index);
len++;
index++;
}else{
if(len>maxLen){
maxLen=len;
}
int dulpIndex=map.get(c);//发生重复字符的前一个位置
while(indexOfFirst<=dulpIndex){
map.remove(s.charAt(indexOfFirst));
indexOfFirst++;
}
len=index-dulpIndex;//替换重复字符后的字符串长度
map.put(c,index);
index++;
}
}
if(maxLen<len)maxLen=len;
return maxLen;
}
}
四、初始结果
运行耗时 | 9ms | 击败 58.02% |
---|---|---|
内存 | 40.2M | 击败 5.20% |
虽然得出了正确的结果,但是运行的时间复杂度和空间复杂度都比较高。
五、优化思路
使用滑动窗口算法进行优化,并可以使用一个cha数组进行数组存储,出现过标记为1,未出现过标记为0.
六、优化代码
class Solution {
public int lengthOfLongestSubstring(String s) {
char[] chs=new char[256];
int left=0;
int right=0;
int maxLen=0;
int len=0;
while(right<s.length()){
int cur=s.charAt(right);
if(chs[cur]==0){
len++;
chs[cur]=1;
}else{
while(s.charAt(left)!=cur){
if(maxLen<len)maxLen=len;
chs[s.charAt(left)]=0;
left++;
len--;
}
left++;
}
right++;
}
if(maxLen<len)maxLen=len;
return maxLen;
}
}
七、优化结果
执行时间 | 3ms | 击败 97.72% |
---|---|---|
消耗内存 | 39.6M | 击败 5.89% |
八、总结
优化过程使用了HashSet和char数组分别进行存储,空间消耗提升效果不太明显,而用char数组进行存储执行效率有明显提升,计算机访问数组通过下标要比其他数据结构更快,滑动窗口+数组提升执行效率效果还是不错的。
滑动窗口还可以用来解决连续数组和问题、最小覆盖字符串问题,之后遇到再详细讨论。