題目:
給定一個字符串,找出不含有重複字符的最長子串的長度。
示例:
給定 "abcabcbb"
,沒有重複字符的最長子串是 "abc"
,那麼長度就是3。
給定 "bbbbb"
,最長的子串就是 "b"
,長度是1。
給定 "pwwkew"
,最長子串是 "wke"
,長度是3。請注意答案必須是一個子串,"pwke"
是 子序列 而不是子串。
解題1:
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
String result = ""; //最長的不重複子串
String str = ""; //子串
for(int i=0;i<len;i++){
for(int j=i+1;j<=len;j++){
str = s.substring(i,j); //嵌套循環截取子串
if(j==len){ //截取帶最後進行對比
result = result.length()<str.length()?str:result;
break;
}else{ //如果沒有到最後就判斷下一位是否在子串中
if(str.indexOf(s.substring(j,j+1))>-1){
result = result.length()<str.length()?str:result;
break;
}
}
}
}
return result.length();
}
}
這是我自己的解法:首先要理解一個思路,如果[i,j]是不重複的,那麼要判斷[i,j+1]是否重複,就只要判斷[j+1]在[i,j]是否存在。個越界檢查。
這裏的計算就是:設'abcabcbb',len=8,
從(0,1)(含左不含右)開始,不等於len,進入else條件,'a'不包括下一個字符'b',依次循環,
到了(0,3)的時候,str="abc",包含下一個字符'c',result="abc",跳出循環,
進入(1,2),str="b",一直到(1,4),包含下一個字符'b',result長度等於str長度,所以result不變。跳出循環,
進入(2,3),str="c",依次循環,最後一直到最後str的長度也沒有超過result。最終得出result爲abc,所以長度爲3
時間複雜度:O(n^2)
空間複雜度:O(n^2)
解法2:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>(); //存放窗口元素
int ans = 0, i = 0, j = 0; //ans不重複子串長度,i是頭節點,j是尾節點
while (i < n && j < n) {
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){ //不包含下一位字符就把尾節點後移一位,窗口+1,並且判斷最長子串的長度
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++)); //包含下一位就把頭節點後移到尾節點的前一位,窗口重新置爲1
}
}
return ans;
}
}
滑動窗口思想:其實這個和上面的思路是一樣的,就是判斷[i,j]和[j+1]是否重複的問題
若重複:頭節點後移到尾節點前一位,滑動窗口大小重定義爲1。
若不重複:滑動窗口頭不變,結尾+1,整個窗口加大1個單位。繼續比較下一個。
時間複雜度:O(n^2);
空間複雜度:O(n);
解法3:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>(); //存放字符數組和索引,key:char,value:index
for (int j = 0, i = 0; j < n; j++) {
if (map.containsKey(s.charAt(j))) {
i = Math.max(map.get(s.charAt(j)), i); //包含下一個字符就把i移到相同字符的後一個位置
}
ans = Math.max(ans, j - i + 1); //判斷長度是否要改變
map.put(s.charAt(j), j + 1); //把字符和索引放進map,重複的會進行覆蓋
}
return ans;
}
}
這是一直利用HashMap解題思路:其實也是判斷[i,j]和[j+1]的問題。
舉個例子:s="abcbadca"; i= 0;j=0
依次把{a:1,b:2,c:3}存入map,此時ans = 3,i=0,j=3;
下一次進入的時候,因爲‘b'已存在map中,所以i會被設爲2,即字符數組中第一個b的後一個位置。同時map裏面的b對應的值會被更新爲4,然後判斷ans的值,同時map裏面的b對應的值會被更新爲4。
依次計算,就可以把最長不重複子串的長度得出。這裏只有一個循環,map的查找時間複雜度爲O(1),比之前的兩種方法都快。
時間複雜度:O(n);
空間複雜度:O(n)
解法4:
public class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
int[] index = new int[128]; // current index of character
for (int j = 0, i = 0; j < n; j++) {
i = Math.max(index[s.charAt(j)], i);
ans = Math.max(ans, j - i + 1);
index[s.charAt(j)] = j + 1;
}
return ans;
}
}
這個解法和上面的HashMap的解法其實是一樣的,只不過這種解法是把之前map的key作爲數組的索引,value作爲數組的值。因爲字符char會被轉爲int值(根據ASCII碼錶對應)。不過這種的優勢在於只要一個128的數組,而不是map的結構。
時間複雜度:O(n);
空間複雜度:O(1);