这是我参与更文挑战的第3天,活动详情查看: 更文挑战
第3天,继续挑战第3题。
题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
思路
一开始读题可能会有一个疑问,这跟这个字符串中有多少个不同的字符有什么区别?示例3就给出了答案,这里的子串,必须是连续的。
其实这题几乎当做是滑动窗口的例题。
可以遍历整个字符串,从每个位置开始找到当前字符串的最长子串,然后在比较长度找出最长的就好了,过程如下图所示:
一个重要的优化点是,当从位置k开始的字符串,找到最长子串Ak...A(k+m-1),那么从位置k+1开始,到位置k+m-1必然是不重复的,这部分在上一步已经算过了。反映到代码上,用一个Set存储了当前子串的长度,计算下一个开始时,可以从Set中把Ak移除,然后从A(k+m)开始判断是否跟Set中的重复。
这整个过程就像是用一个框来把最长子串框出来,这个框就想一个窗口,从左到右移动了一遍,所以滑动窗口这个名称还是挺形象的。
注意
注意点在示例4里面已经给出了,注意数组未空的情况。 还有一个优化点是,剩余字符串的长度不足已经找到的最长子串的长度时,不需要继续了,比如上图中的6、7这2行,当前最长子串长度是3,还剩余未遍历长度是2,此时就可以不继续遍历了,因为剩余的即使全部不重复,也不可能更长了。
Java版本代码
class Solution {
public int lengthOfLongestSubstring(String s) {
int length = s.length();
if (length == 0) {
return 0;
}
Set<Character> set = new HashSet<>();
int result = 0;
int end = -1;
for (int startIndex = 0; startIndex < length; startIndex++) {
// 剩余字符串的长度不足已经找到的最长子串的长度时,不需要继续了,因为剩余的即使全部不重复,也不可能更长了
if (length - startIndex <= result) {
break;
}
// 当前最大长度等于上一次字串的长度-1,上一个字串长度为 end - (startIndex - 1) + 1
int max = end - (startIndex - 1) + 1 - 1;
// startIndex > 0的时候,需要把startIndex - 1处的字符移走
if (startIndex > 0) {
set.remove(s.charAt(startIndex - 1));
}
while (end < (length - 1) && !set.contains(s.charAt(end + 1))) {
set.add(s.charAt(end + 1));
max++;
end++;
}
if (max > result) {
result = max;
}
}
return result;
}
}