DESC:
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。示例 4:
输入: s = ""
输出: 0提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
CODE1:
JAVA:
class Solution { public int lengthOfLongestSubstring(String s) { if (s == null || s.length() == 0) { return 0; } int n = s.length(); int pre=0, afe=0, res=0; Map<Character, Integer> map = new HashMap(); while (pre<n) { char c = s.charAt(pre); if (map.containsKey(c)) { int index = map.get(c); afe = Math.max(index, afe); } res = Math.max(res, pre-afe+1); map.put(c, pre+1); pre++; } return res; } } class Solution { public int lengthOfLongestSubstring(String s) { if (s == null || s.length() == 0) { return 0; } int n = s.length(); int start=0, res=0; Map<Character, Integer> map = new HashMap(); for (int pre=0;pre<n;pre++) { char c = s.charAt(pre); if (map.containsKey(c)) { start = Math.max(start, map.get(c)); } res = Math.max(res, pre-start+1); map.put(c, pre+1); } return res; } }
NOTE1:
- hashmap记录所以字符的位置,为了统一计算长度方便,记录位置索引+1,表示出现重复字符时,从第一个字符下一个位置作为起始位置计算,尾部是第二个重复字符,如[a,..,a]区间,遍历到a出现重复,我们通过(a,..a]、[a+1,..,a]方式计算长度,而非[a,..a)、[a,..,a-1],这里主要是和无重复字符时计算长度公式保持一致,即[aft,pre]=pre-afe+1;
- 遍历字符,使用双指针记录起始位置,并计算最大长度,遇到重复字符,获取重复字符开始的位置,与之前的起始位置比较,取最近计算最大长度;如【a,b,..,b,a】遇到第二个a时,起始索引是max(a+1,b+1);
CODE2:
JAVA:
class Solution { public int lengthOfLongestSubstring(String s) { if (s == null || s.length() == 0) { return 0; } int n = s.length(); int end=-1, res=0; Set<Character> set = new HashSet(); for (int i=0;i<n;i++) { if (i!=0) { set.remove(s.charAt(i-1)); } while (end+1<n && !set.contains(s.charAt(end+1))) { set.add(s.charAt(end+1)); end++; } res = Math.max(res, end-i+1); } return res; } }
class Solution { public int lengthOfLongestSubstring(String s) { if (s == null || s.length() == 0) { return 0; } int n = s.length(); int end=0, res=0; Set<Character> set = new HashSet(); for (int i=0;i<n;i++) { if (i!=0) { set.remove(s.charAt(i-1)); } while (end<n && !set.contains(s.charAt(end))) {//重要,注意end<n条件及end定义位置 set.add(s.charAt(end)); end++; } //res = Math.max(res, end-1-i+1); res = Math.max(res, end-i); } return res; } }
NOTE2:
- 滑动窗口思想,遍历求以目前字符为起始字符的最大无重复子串,遍历过程中,移除窗口左边界,右边界继续向右探索,右边界是全局变量,因为窗口内肯定是无重复的;
- 难点在于右边界的定义初始化,这里初始化为-1,起始end+1才代表右边界,目的和code一样,统一长度计算公式,兼容无重复字符,[start,end]前闭后闭,因为向右探索中,索引位置为end+1,所以遇到重复字符后,取右边界闭区间位置loc=end+1-1=end;这个地方让人有点迷,还不如code1好想