給定一個字符串 s ,請你找出其中不含有重復字符的 最長 子串 的長度。
示例 1:
輸入: s = “abcabcbb”
輸出: 3
解釋: 因為無重復字符的最長子串是 “abc”,所以其長度為 3。
示例 2:
輸入: s = “bbbbb”
輸出: 1
解釋: 因為無重復字符的最長子串是 “b”,所以其長度為 1。
示例 3:
輸入: s = “pwwkew”
輸出: 3
解釋: 因為無重復字符的最長子串是 “wke”,所以其長度為 3。請注意,你的答案必須是 子串 的長度,“pwke” 是一個子序列,不是子串。
提示:
0 <= s.length <= 5 ? 10 4 5 * 10^4 5?104
s 由英文字母、數字、符號和空格組成
知識點:
滑動窗口、哈希表、字符串
根據官解,有以下結論:
①一旦涉及出現次數 ,就需要使用哈希表。
字符串類型的題:key=字符,value=出現次數
②一旦涉及子串,通常能通過滑動窗口進行動態擴張
解:
首先,對空字符串進行處理,直接返回0,提高效率。
然后,定義變量max
存儲最長子串的長度。定義一個HashMap,存儲每個字符在字符串中出現的下標。此外,定義一個start
變量,表示滑動窗口的起始下標。
接著,for循環遍歷整個字符串,通過s.charAt(i)
獲取當前遍歷的字符。
在for循環內:
1、若當前遍歷的字符沒在map中,那么通過map.put(s.charAt(i), i);
將當前字符加入map,能保證滑動窗口內無重復字符,這里鍵值對的value=i,表示的是當前遍歷的字符的下標。
2、若當前遍歷的字符在map中,我們要進行if-else判斷:
①若當前遍歷的這個重復字符,目前在滑動窗口內,則移動滑動窗口的左邊界start
到當前遍歷的字符的下一個位置。原因是:既然重復的字符在滑動窗口內,如果左邊界還是在滑動窗口內的這個字符的位置,或者是滑動窗口內的這個字符的左側,整個滑動窗口還是重復的,沒有必要這么做,因此我們直接讓start
移動到map.get(s.charAt(i)) + 1
的位置。
這里以測試樣例3說明原因中的第一點。第二點根據文字描述即可想象出情況,就不例舉了。
②若當前遍歷的這個重復字符,在滑動窗口前面,則不處理。因為我們看的就是滑動窗口內的情況,不考慮滑動窗口外的情況。
在上面的重復字符處理結束后,通過map.put(s.charAt(i), i);
將當前字符加入map。
最后,我們通過Math.max(max, i - start + 1)
獲取每次滑動窗口遍歷的最長子串的長度。
最終返回max
變量。
class Solution {public int lengthOfLongestSubstring(String s) {//空字符串直接返回if (s.length() == 0) {return 0;}//最長子串長度int max = 0;//HashMap存儲每個字符出現在字符串中的下標Map<Character, Integer> map = new HashMap<>();//滑動窗口起始下標int start = 0;//end = i: 隱式表示滑動窗口結束下標//遍歷字符串的每個字符for (int i = 0; i < s.length(); i++) {//已出現過當前遍歷的字符if (map.containsKey(s.charAt(i))) {//若當前遍歷的這個重復字符,目前在滑動窗口內,則移動start到這個重復字符的下一個位置if (map.get(s.charAt(i)) >= start && map.get(s.charAt(i)) <= i) {start = map.get(s.charAt(i)) + 1;}//若當前遍歷的這個重復字符,在滑動窗口前面,則不處理else if (map.get(s.charAt(i)) < start) {start = start;}}//將當前遍歷字符加入map//1.當前遍歷字符不在map中,則滑動窗口內必無重復字符//2.當前遍歷字符在map中,而此時已完成滑動窗口邊界的處理,此時字符也不會在滑動窗口中重復出現map.put(s.charAt(i), i);//獲取當前子串的長度max = Math.max(max, i - start + 1);//數組下標從0開始,則子串長度需要加1}return max;}
}
引用:
- java遍歷String字符串的方法