题目:
给定一个字符串 s
,请你找出其中不含有重复字符的最长子串的长度。
示例:
示例 1:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke"是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s
由英文字母、数字、符号和空格组成
分析:
首先理解什么是字串,像:
a、b、c、a、b、c、b、b这种单个子串
ab、bc、ca、cb、bb这种两个字符组成的子串
abc、bca、cab、bcb、cbb这种三个字符组成的子串
abca、bcab、cabc、abcb这种四个字符组成的子串
abcab、bcabc、cabcb、abcbb这种五个字符组成的子串
abcabc、bcabcb、cabcbb这种六个字符组成的子串
abcabcbbcabcbb这种七个字符组成的子串
abcabcbb这种八个字符组成的子串
然后理解什么是最长子串:就是子串中个数最多的那个子串,例如abcabcbb
最长不重复子串,即一个最长子串中没有重复的字符,例如abc,bca,cab。
那么我们就可以得到,当我们从头遍历字符串时,记录每一个字符是否出现过,若没有则标记,反之则跳出遍历,获得当前子串长度,依次类推,直至所有字符遍历完。
实现:
方法一:暴力求解
class Solution {
public int lengthOfLongestSubstring(String s) {
// 用于存储最长字串的长度
int maxLen = 0;
// 外层循环,从字符串第一个字符开始
for (int i = 0; i < s.length(); i++) {
// boolean数组,用来标记字符是否出现过
boolean[] booleans = new boolean[300];
// 内层循环,从当前字符向前遍历
for (int j=i;j>=0;j--){
// 如果字符已经出现过,结束内层循环
if(booleans[s.charAt(j)]) break;
// 标记当前字符已经出现过
booleans[s.charAt(j)] = true;
//更新最长字串长度
maxLen = Math.max(maxLen, i-j+1);
}
}
return maxLen;
}
}
方法二:滑动窗口
首先,经过我们前面的分析和暴力求解,我们知道,我们其实求的是一个最长不重复子串的区间,那么我们可以考虑两个指针,一个指向区间头,一个指向区间尾。
我们让头指针不动,尾指针向后扩展,直至出现重复的元素,头指针向后扩展,调整左边界。
class Solution {
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
int maxLen = 0;
int left = 0; // 滑动窗口的左边界
for (int right = 0; right < s.length(); right++) {
// 当前字符已经在窗口内,移动左边界
while (set.contains(s.charAt(right))) {
set.remove(s.charAt(left++));
}
// 将当前字符加入窗口
set.add(s.charAt(right));
// 更新最长不重复子串的长度
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
}