hello,大家好,我是腿短快跑,今天的主题是算法,这是我昨天刷的一道算法题。欢迎大家一起沟通有没有更好的实现方法
本文最先发布于我的个人博客,简书为同步发布,如果有需要请访问 腿短快跑的个人博客
题目描述
给定一个字符串 s
,请你找出其中不含有重复字符的最长子串
的长度。
示例
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示
- 0 <= s.length <= 5 * 10⁴
- s 由英文字母、数字、符号和空格组成
题解
滑动窗口
思路
- 左右两个指针表示滑动窗口的左右边界
- 在每一次判断中,我们将左指针向右移动一个,表示从下一个元素开始判断,同时右指针不断的向后移动,在此过程中需要保证左右指针中间的字符没有重复,移动结束后记录左右指针之间的距离,判断是否包含重复字符可以采用哈希表来判断
- 将最长的左右指针之间的距离返回
实现
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s == null || s.length() <= 0) {
return 0;
}
char[] chars = s.toCharArray();
Set<Character> hash = new HashSet<>();
int max = 0;
// i表示左指针,j表示右指针
for (int i = 0; i < chars.length; i++) {
hash.clear();
hash.add(chars[i]);
for (int j = i + 1; j < chars.length; j++) {
// 判断是否存在
if (hash.contains(chars[j])) {
break;
} else {
hash.add(chars[j]);
}
}
if (max < hash.size()) {
max = hash.size();
}
}
return max;
}
}
复杂度分析
时间复杂度:,因为我们每次都是从左指针位置开始循环,最复杂的情况每次都需要遍历到结尾
空间复杂度:,主要为数组和哈希表的开销
性能
执行耗时:66 ms,击败了12.53% 的Java用户
内存消耗:41.9 MB,击败了12.89% 的Java用户
滑动窗口优化
思路
上述方法中,右指针每次都是从左指针的位置开始移动,右指针存在大量的重复操作,我们可以从上一次的位置继续向后移动,只需要哈希表数据每次在左指针向后移动的时候移除上一次的左指针的元素即可
实现
class Solution {
public int lengthOfLongestSubstring(String s) {
// 哈希集合,记录每个字符是否出现过
Set<Character> occ = new HashSet<Character>();
int n = s.length();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.remove(s.charAt(i - 1));
}
while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) {
// 不断地移动右指针
occ.add(s.charAt(rk + 1));
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
}
复杂度
时间复杂度:,左右指针各会将每个字符遍历一遍
空间复杂度:,主要为哈希表的开销
性能
执行耗时:5 ms,击败了60.30% 的Java用户
内存消耗:41.2 MB,击败了79.66% 的Java用户