LeetCode—3. Longest Substring Without Repeating Characters
题目
https://leetcode.com/problems/longest-substring-without-repeating-characters/description/
找出一个字符串的最长不重复子串,注意是子串不是子序列。返回最长的长度。
思路及解法
第一种方法,暴力的解法。直接看代码。超时。时间复杂度 O ( n 3 ) O(n^3) O(n3),空间复杂度 O ( m i n ( m , n ) ) O(min(m,n)) O(min(m,n)),m是字符的种类数,n是字符串的长度。
第二种方法,双指针方法。利用两个指针建立一个窗口,窗口内字符用哈希集合储存,用以保证窗口内的字符不出现重复。不重复的情况,将窗口向右扩展,出现重复的字符,从左向右缩小窗口。变量count用来表示当前窗口的长度,变量maxlen表示整个字符串的最大长度。
时间复杂度
O
(
2
n
)
=
O
(
n
)
O(2n)=O(n)
O(2n)=O(n)
空间复杂度
O
(
m
i
n
(
m
,
n
)
)
O(min(m,n))
O(min(m,n)), m是字符的种类数,n是字符串的长度。
第三种方法,对双指针方法的优化。上面双指针时间复杂度是2n,我们可以优化到n。利用hashmap存储字符和其对应的索引,这样在遇到重复字符时,不需每次从左向右只缩小一个位置,而是窗口左端直接跳到重复字符的最大索引上。
代码
暴力解法
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
if(len==0 || len==1) return len;
int ans = 0;
for(int i=0; i<len-1; i++){
for(int j=i+1; j<=len; j++){
if(allUnique(i, j, s)){
ans = Math.max(ans, j-i);
}else{
break;
}
}
}
return ans;
}
public boolean allUnique(int start, int end, String s){
Set<Character> set = new HashSet<>();
for(int i=start; i<end; i++){
Character tmp = s.charAt(i);
if(set.contains(tmp)) return false;
set.add(tmp);
}
return true;
}
}
双指针法
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
if(len==0 || len==1) return len;
int count = 0, maxLen = 0;
int i=0, j=0;
HashSet<Character> set = new HashSet<>();
while(i<len && j<len){
if(set.contains(s.charAt(j))){
set.remove(s.charAt(i++));
count--;
}else{
set.add(s.charAt(j++));
count++;
}
maxLen = Math.max(maxLen, count);
}
return maxLen;
}
}
双指针优化
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
if(len==0 || len==1) return len;
int ans = 0;
Map<Character, Integer> map = new HashMap<>();
for(int j=0, i=0; j<len; j++){
if(map.containsKey(s.charAt(j))){
i = Math.max(i, map.get(s.charAt(j)));
}
ans = Math.max(ans, j-i+1);
map.put(s.charAt(j), j+1);
}
return ans;
}
}