给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 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 由英文字母、数字、符号和空格组成
解法一:暴力破解
思路:以字符串中的每一个字符作为起点,依次遍历字符串中起点字符右侧的字符,如果没有重复就放入HashSet中,如果字符和HashSet中的字符有重复就记录HashSet的长度,和最长长度比较,保留最长长度,依次类推,直到所有的字符遍历完,最终得到最长子串长度。
示例:String s = "abcabcdb";
遍历得到的子串有:"abc","bca","cab","abcd","","bcd","cdb","db"
很容易看到最长子串为 "abcd"。
这种解法的时间复杂度是O(n^2),空间复杂度是O(n)。
编码如下:
public static int subString2(String s){ int ans = 0;//最大长度 HashSet<Character> occ = new HashSet<Character>();//存储无重复字符串 for(int i=0;i<s.length()-1;i++){ occ.add(s.charAt(i)); for (int j=i+1;j<s.length() && !occ.contains(s.charAt(j));j++){ occ.add(s.charAt(j)); } ans = Math.max(occ.size(),ans); occ.clear(); } return ans; }
解法二:使用滑动窗口
思路:使用HashSet来存储滑动窗口中的字符,遍历字符串中的字符,滑动窗口有左指针 left 和右指针 right,right 向右移动,指向的元素判断是否在HashSet中有重复的元素,如果没有就把该元素放入滑动窗口中,如果有就把重复元素左侧所有的元素移除滑动窗口,即left指针向右移动,知道没有重复元素。记录每次滑动窗口的长度,最长的即为无重复字符最长子串的长度。解题过程如下所示:
可以看到最长子串在第六步产生,此时滑动窗口中的字符有 a b c d,长度是4。
编码实现:编码实现有很多种方式,每个人的风格都不一样,这里提供两种编码风格
编码一:
public static int subString(String s){ HashSet<Character> occ = new HashSet<Character>(); int left = 0;//滑动窗口的左指针 int right = 0;//滑动窗口的右指针 int ans = 0;//最长子串的长度 while(left<=right && right<s.length()){ if (!occ.contains(s.charAt(right))){ occ.add(s.charAt(right++)); ans = Math.max(ans,right-left); } else { occ.remove(s.charAt(left++)); } } return ans; }
编码二:
public static int subString(String s){ HashSet<Character> occ = new HashSet<Character>(); int left = 0;//滑动窗口的左指针 int right = 0;//滑动窗口的右指针 int ans = 0;//最长子串的长度 for(int i=0;i<s.length();i++) { char t = s.charAt(i); while (occ.contains(t)) { occ.remove(s.charAt(left++)); } occ.add(t); right++; ans = Math.max(ans,right-left); } return ans; }
这种解题方式时间复杂度是 O(n),空间复杂度是O(n)。