3. 无重复字符的最长子串 - LeetCode
3. 无重复字符的最长子串
最初的想法
class Solution {
public int lengthOfLongestSubstring(String s) {
int ans = 0, i = 0, j = 0;
HashSet<Character> set = new HashSet<>();
while(i < s.length()){
while(j < s.length() && !set.contains(s.charAt(j))){
set.add(s.charAt(j));
j++;
}
ans = Math.max(ans, j - i);
if(j == s.length()){
break;
}
while(set.contains(s.charAt(j))){
set.remove(s.charAt(i));
i++;
}
}
return ans;
}
}
很自然的,用两个指针表示子串头尾,用HashSet记录是否重复。但最终的速度不尽如人意,只超过35%的提交记录。
小优化——空间换时间
- 首先,i + ans >= s.length()就可以停止循环了,因为不可能出现更长的子串了。
- 其次,字符ASCLL码只有128种,可以用数组,虽然可能占用更大的空间,但查询、插入、删除等操作都只用O(1)的时间复杂度。
于是有了如下代码:
class Solution {
public int lengthOfLongestSubstring(String s) {
int ans = 0, i = 0, j = 0;
boolean[] set = new boolean[128];
while(i + ans < s.length()){
while(j < s.length() && !set[s.charAt(j)]){
set[s.charAt(j)] = true;
j++;
}
ans = Math.max(ans, j - i);
if(j == s.length()){
break;
}
while(set[s.charAt(j)]){
set[s.charAt(i)] = false;
i++;
}
}
return ans;
}
}
这时用时超过了96%的提交记录,较为满意。
更多的空间换时间——记录字符上一次出现的位置
上面用的boolean数组记录了字符之前是否出现,但我们如果用int数组记录字符上一次出现的位置,就可以直接计算出子串长度,不需要两个指针,可以大大提升运行效率。
class Solution {
public int lengthOfLongestSubstring(String s) {
int[] last = new int[128];
for(int i = 0; i < 128; i++){
last[i] = -1;
}
int ans = 0, start = 0, n = s.length();
for(int i = 0; i < n; i++){
int index = s.charAt(i);
start = Math.max(start, last[index] + 1);
ans = Math.max(ans, i - start + 1);
last[index] = i;
}
return ans;
}
}
最终运行时间超过100%的提交记录。
可以看到改进后的代码较为简洁,先枚举子串结束位置,直接通过上一次出现的位置,计算出最长不重复子串的起始位置,省时省力。

浙公网安备 33010602011771号