题目:给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
示例 1:
输入:s="abcabcbb"
示例 2:
输入:s="bbbbbb"
示例 3:
输入:s=""
解法一:滑动窗口
我们用滑动窗口的算法,设置双轨指针。左指针指向子字符串开始位置,右指针指向子字符串结束位置。如果两个指针中没有重复的字符,右指针右移一位。如果右指针指向的字符出现重复,则左指针右移一位。
image
image
private static int lengthOfLongestSubstring (String s) {
if (StringUtils.isBlank(s)) {
return 0;
}
Set<Character> occ = new HashSet<Character>();
// 最大长度 (原始保本)
int maxLength = s.length();
// 右指针,指向滑动窗口的右边界
int rPoint = 0;
// 最长的子字符串长度
int childLength = 0;
for (int i = 0; i < maxLength; i++) {
// 左指针每次移动都要从occ中移除对应的元素
if (i != 0) {
occ.remove(s.charAt(i - 1));
}
// 右指针只要没有相同的元素就不断右移
while (rPoint < maxLength && !occ.contains(s.charAt(rPoint))) {
occ.add(s.charAt(rPoint));
rPoint++;
}
childLength = Math.max(childLength, rPoint - i);
/**
* 优化点:当剩余的字符串判断结果已经不会影响childLength时我们可以终止计算。
* 判断方式:右指针和最后一个字符之间的距离 + 左右指针之间的间隔 <= 当前的最长子字符串长度
**/
if (maxLength - i + 1 <= childLength) {
break;
}
}
return childLength;
}
解法二:滑动窗口优化
其后我们发现,其实左指针每次右移一格其实是很浪费的,多出了不少的迭代。因此我们换成HashMap来记录前面重复的字符及对应的坐标,只要在出现重复字符时将左指针指向的位置改为前面重复字符的下一位即可。
image
private static int lengthOfLongestSubstring (String s) {
Map<Character, Integer> occ = new HashMap<Character, Integer>();
// 最大长度 (原始保本)
int maxLength = s.length();
// 左指针,指向滑动窗口的左边界
int lPoint = 0;
// 最长的子字符串长度
int childLength = 0;
for (int i = 0; i < maxLength; i++) {
char c = s.charAt(i);
if (occ.containsKey(c)) {
// 已经具有重复的字符,直接将左指针指向前面相同元素的下一位
lPoint = Math.max(occ.get(c) + 1, lPoint);
}
occ.put(c, i);
childLength = Math.max(childLength, i - lPoint + 1);
}
return childLength;
}