题目详情
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
暴力解法
面对一道题,一上来还是要稍微想一下暴力解法的,不然直接改进,很容易出问题。在有暴力解法的基础上,进行改进,去重复,取巧,很快,bug也很少。
先来将暴力解法,很容易想到啦,遍历字符串,在每个点,都往后一直遍历,把每个字符加入set,直到遇到重复的字符为止。
代码如下:
class Solution {
public int lengthOfLongestSubstring(String s) {
//首先暴力解法,每次用set与后面比较
Set<Character> set = new HashSet<>();
int maxLen =0;
for(int i=0; i<s.length(); i++){
int len =0;
for(int j = i; j<s.length(); j++){
char cur_ch = s.charAt(j);
if(set.contains(cur_ch)){
break;
}
set.add(cur_ch);
len++;
}
maxLen = Math.max(len,maxLen);
set.clear();
}
return maxLen;
}
}
时间复杂度都到O(n^2)了,运行时间108ms,打败了15%哈哈哈。
那么下面来改进。
从暴力解法改进到快速解法
这里是借助start指针和set同时来完成。
我们用图来看嘛,这样更清晰一些。
假设当前我已经从i点开始找到j-1,这是一个不重复的字符子串,但是j号位重复了。
此时如果按照暴力解法的逻辑,我需要回到i+1,重置set,然后继续一个个查询。
但duck不必,我们直接从start开始往后遍历,将遍历到的字符从set中删去,知道遇到了在我们的子串中的X。
此时,我们并不回到i+1去,而是去到后一个X的下一位,并将start指针移动到前一个X的下一位因为从这个位置到第二个X,我们可以肯定这其中没有重复字符。此时,我们省去了很多开销,不用再重复比较。
思想主要是之前看kmp算法和manacher想到的,就是去重嘛。
该解法就快很多了5ms,击败87%,空间也有77%,还行吧。应该算是思路比较清晰的那种。
快速解法代码实现
开头注释是我从使用暴力解到最终快速解法的思路过程,可以直接忽略,这里不删是便于之后我自己复习。
public int lengthOfLongestSubstring(String s) {
//首先暴力解法,每次用set与后面比较
//当然了,暴力解法最慢了,所以要想一想如何改进
//因为是关于字符串的,所以要想想能不能用manacher算法和kmp算法
//肯定都不能用,不过有没有新的思路呢,能不能排除掉一些重复的?
//有点难想啊,这个要是追求o(n)就真有点难
//想到了,不用set,而用map来存储每个值的索引,这样的话,我就不用每次只移动一个
//我只要移动到重复元素(前者)的后面一位就可以了。
//同时,我要将所有的索引小于重复元素(前者)的全部删掉
//思路详细的写一下,这里要有一个指针,来记录下当前的起点
//后来起点的更新为重复元素的后一个索引
//显然还能改进,因为这里用set会更好,你没有必要用map,因为你始终都是要删除它们的,
//所以直接从start开始往后,找到cur_ch
Set<Character> set = new HashSet<>();//键是值,value是索引
int maxLen =0;
int start = 0;
int len;
for(int i =0; i<s.length(); i++){
len = i - start;
for(int j = i; j<s.length(); j++){
char cur_ch = s.charAt(j);
if(set.contains(cur_ch)){//发现重复
//删除之前的点
int index = start;
while(s.charAt(index)!=cur_ch){
set.remove(s.charAt(index));
index++;
}
start = index+1;//重置起点
//更新i
i = j;
maxLen = Math.max(len,maxLen);
len = i-start+1;
break;
}
set.add(cur_ch);
len++;
}
maxLen = Math.max(len,maxLen);
}
return maxLen;
}
}