題干:
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.
Given "bbbbb"
, the answer is "b"
, with the length of 1.
Given "pwwkew"
, the answer is "wke"
, with the length of 3. Note that the answer must be a substring, "pwke"
is a subsequence and not a substring.
解法一:
列出所有字符串,並判斷是否有重復字符。(最直觀的方法,但是運算量極大,不要用)
public class Solution { public int lengthOfLongestSubstring(String s) { int n = s.length(); int ans = 0; for (int i = 0; i < n; i++) for (int j = i + 1; j <= n; j++) if (allUnique(s, i, j)) ans = Math.max(ans, j - i); return ans; } public boolean allUnique(String s, int start, int end) { Set<Character> set = new HashSet<>(); for (int i = start; i < end; i++) { Character ch = s.charAt(i); if (set.contains(ch)) return false; set.add(ch); } return true; } }
解法二:
利用HashSet做划窗來判斷字符唯一性。(用這個)
public class LengthOfLongestSubstring3 { /* * 思路: * 用Set做一個范圍為 [i,j)的划窗(利用Set檢查char的唯一性)。 * 在確定s[i,j-1]沒有重復字符的情況下,只需檢查s[j]是否存在於s[i,j-1]中就可以了。 * 如果s[j]存在於s[i,j-1]中,那么划窗變為 [i+1,j),繼續檢查。 * **/ public int lengthOfLongestSubstring(String s) { int n = s.length(); Set<Character> set = new HashSet<>(); int ans = 0, i = 0, j = 0; while (i < n && j < n) { // try to extend the range [i, j] if (!set.contains(s.charAt(j))) { // 此處是根據角標j來遍歷,一個一個的往里存 set.add(s.charAt(j++)); ans = Math.max(ans, j - i);//注意這里為什么是j-1而不是j-i+1呢?因為j已經++了 } else { set.remove(s.charAt(i++)); } } return ans; } }
思考:整個流程中,角標是如何變化的?以及while(i<n & j<n)配合i++和j++這種代碼結構。
解法三:
利用HashMap建立{字符-索引}的映射,降低找重復時的時間復雜度(優解但是不太好想)
public class Solution { /* * 思路: 使用HashMap存放{字符-索引}的映射,當我們發現重復的字符時,直接跳過這些字符。 * 通過遍歷j來循環。如果遍歷到j時,s[i,j)中有重復元素s[j],我們不再一點一點移動i的位置了, * 若重復元素角標為j',直接跳過[i,j′]這個區間即可,直接將i賦值為j'+1。 * */ public int lengthOfLongestSubstring(String s) { int n = s.length(), ans = 0; // Hashmap中存放的是 字符-索引 Map<Character, Integer> map = new HashMap<>(); // try to extend the range [i, j] for (int j = 0, i = 0; j < n; j++) { if (map.containsKey(s.charAt(j))) { // 如果map里已經有這個index[j]處的char,將i賦成j+1。 i = Math.max(map.get(s.charAt(j)) + 1, i); } // 注意這里的max,適用於“abba”這種情況,“a”雖然存在於map中了,但i也不移動 ans = Math.max(ans, j - i + 1); // 將char加入哈希表中,value為索引值j。 map.put(s.charAt(j), j); } return ans; } }
思考:怎么用一次循環完成整個算法的?想清楚整個流程中兩個索引i,j是怎么變化的。
*注意max*