题干:
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*