Leetcode链接:https://leetcode.com/problems/longest-substring-without-repeating-characters/
以下是我的代码使用集:
class Solution {
public int lengthOfLongestSubstring(String s) {
char[] arr = s.toCharArray();
int n = s.length();
int left = 0, right = 0, ans = 0;
Set<Character> set = new HashSet<>();
if (s == null || s.length() == 0) return ans;
while (left < n && right < n) {
if (!set.contains(arr[right])) set.add(arr[right++]);
else {
while (set.contains(arr[right])) {
set.remove(arr[left++]);
}
}
ans = Math.max(ans, right - left);
}
return ans;
}
}
我的理解,例如,[a,b,c,a]
,当right = 3
,left = 0
,然后设置删除'a'(其索引为0(,left++
,所以,left = 1
,然后,它应该返回(3-1(+1。
不清楚为什么ans
的重新计算应该是外部while
循环的最后一行,无论set.contains
检查如何,都会执行该循环。
看,子字符串扩展的唯一方法是在if
分支中:
if (!set.contains(arr[right])) set.add(arr[right++])
这基本上意味着";好的,所以我们可以将当前子字符串再扩展一次,还没有重复;。
虽然right - left
表达式似乎少了该子字符串的真实长度一(如果right和left=0,则子字符串的长度实际上是1(,但这是由前一行中的增量(right++
(补偿的。
所以这个表达式实际上应该读作";该循环的初始右边-该循环的起始左边+1"(,在您的示例中,最大值是在"c"检查时达到的,而不是在"a"检查时。
现在,对于'else'分支,不重复的子字符串永远不会被扩展:所有代码所做的就是通过增加left
的值(至少增加一个:如果我们到达那里,left++
至少执行一次(来收缩它(从它的左侧(。
但如果right
不增加,left
只能上升,那么根据定义,right - left
的值只能下降,对吧?这使得如果Math.max
在else
分支代码之后立即执行,那么它注定会失败。
[a,b,c,a]的迭代
- 集合{a},l=0,r=1,max=1
- 设置{a,b},l=0,r=2,max=2
- 设置{a,b,c},l=0,r=3,max=3
- 集合{b,c},l=1,r=3,max=3
- 集合{b,c,a},l=1,r=4,max=3
这应该有助于了解该算法的工作原理。