题目
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc"
,所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b"
,所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是"wke"
,所以其长度为 3。请注意,你的答案必须是 子串 的长度,"pwke"
是一个子序列,不是子串。
思想(滑动窗口)
滑动窗口是一种在数组或字符串上进行迭代的算法,通过维护一个子数组或子串,该子数组或子串的大小在迭代过程中可以变化。该子数组或子串的起始位置通过"滑动"进行调整,从而找到符合特定条件的解。
滑动窗口的窗口大小是动态变化的,当发现重复字符时,通过调整窗口的起始位置来实现。这样,你能够在遍历字符串的过程中找到不包含重复字符的最长子串,同时利用 `max` 变量来记录最大长度。
滑动窗口是解决一类子串或子数组问题的常见思想,通常用于解决找到不重复元素的最长子串或子数组的问题。这种方法的优势在于它可以在线性时间内解决问题,而不需要嵌套循环。
算法分析
1. 初始化变量:`max` 用于存储最长子串的长度,`index` 用于跟踪重复字符的索引,空字符串 `str` 用于存储当前子串。
2. 使用 for 循环遍历输入字符串 `s` 中的每个字符。
3. 在循环内部,通过使用 `indexOf` 方法检查当前字符 `ch` 是否已经存在于当前子串 `str` 中。如果存在(`index != -1`),则表示找到了重复的字符。
4. 如果找到重复的字符,通过比较当前子串 `str` 的长度和当前最大长度 `max` 来更新 `max` 长度。然后,通过删除从子串开头到重复字符(包括重复字符)的字符来更新子串 `str`,并将当前字符 `ch` 连接到子串中。
5. 继续循环。
6. 如果未找到重复字符,简单地将当前字符 `ch` 连接到子串 `str` 中。
7. 循环结束后,比较最终子串 `str` 的长度和当前最大长度 `max`,以确保最后一个子串也被考虑在内。
8. 返回最大长度。
class Solution {public int lengthOfLongestSubstring(String s) {int max=0;int index=-1;String str="";for(int i=0;i<s.length();i++){char ch=s.charAt(i);index=str.indexOf(ch);if(index!=-1){max=Math.max(str.length(),max);str=s.substring(i-(str.length()-1-index),i+1);continue;}str=str+ch;}max=Math.max(str.length(),max);return max;}
}
运行结果
总的时间复杂度为 O(n)
击败百分之5%的对手。。。emmm被我击败南坪
算法调优
这时官方的解法,思想也是滑动窗口,来看看有什么区别:
时间复杂度是O(n^2)
它采用一个Set集合,然后左指针指向i ,右指针指向rk
rk从一个开始逐个遍历,增加不同的值加入集合,
一旦有相同的值,就说明此i指向的值的最长长度已经到了,让左指针i++,指向下一个值。
rk不用更新回去,因为从i到rk是不重复的,那从i+1到rk也肯定是不重复的,所以rk不用更新回去,set集合也不用清空。
每次左指针移动前,要更新最长的长度
Set<Character> occ = new HashSet<Character>();
: 创建一个哈希集合 occ
,用于记录当前窗口中字符的出现情况。
int rk = -1, ans = 0;
: 定义右指针 rk
初始值为 -1,用于表示当前窗口的右边界。ans
用于记录最长子串的长度。
occ.remove(s.charAt(i - 1));
: 在每次移动左指针时,从哈希集合中移除左指针所指向的字符,表示该字符不再属于当前窗口。
while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) { ... }
: 在每次移动右指针时,不断地向右移动,直到遇到重复字符或者到达字符串的末尾。在移动的过程中,将新的字符加入哈希集合。
ans = Math.max(ans, rk - i + 1);
: 在每一步迭代中,更新最长子串的长度。
class Solution {public int lengthOfLongestSubstring(String s) {// 哈希集合,记录每个字符是否出现过Set<Character> occ = new HashSet<Character>();int n = s.length();// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动int rk = -1, ans = 0;for (int i = 0; i < n; ++i) {if (i != 0) {// 左指针向右移动一格,移除一个字符occ.remove(s.charAt(i - 1));}while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {// 不断地移动右指针occ.add(s.charAt(rk + 1));++rk;}// 第 i 到 rk 个字符是一个极长的无重复字符子串ans = Math.max(ans, rk - i + 1);}return ans;}
}
时间耗时:6ms确实比我块的多,但是时间复杂度很大。懂得都懂