给定一个字符串s
,请你找出其中不含有重复字符的最长子串的长度。
示例1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是子串的长度,"pwke" 是一个子序列,不是子串。
提示:0 <= s.length <= 5 * 10^4;s 由英文字母、数字、符号和空格组成。
解决方案
方案一:滑动窗口(自己想的)
因为涉及到字符串,并且要考虑的是子串而不是子序列(子串必须是连续的,天生适合滑动窗口的滑动操作)
class Solution {
public int lengthOfLongestSubstring(String s) {
// base case
if(s == null) {
return 0;
}
char[] charArray = s.toCharArray();
if(charArray.length == 0 || charArray.length == 1) {
return charArray.length;
}
// 用一个队列存储当前滑动窗口中的字符
Queue<Character> queue = new LinkedList<>();
// 滑动窗口的左右边界(在charArray中的index)
int l = 0;
int r = 1;
// 设置初始条件
int longestLength = 1;
queue.add(charArray[0]);
// 滑动条件:左右边界都不超过charArray的长度 且 右边界不能在左边界的左边
while (l < charArray.length && r < charArray.length && l <= r) {
// 判断这个新进来的字符有没有在从l开始的这个子串中
if(!queue.contains(charArray[r])) {
// 如果不在就与最大长度比较
longestLength = longestLength >= (r-l+1) ? longestLength : (r-l+1);
// 把新的字符加进去
queue.add(charArray[r]);
r++;
} else {
// 如果重复了就判断是不是子串的第一个字符,即是否是l位置上的字符
if(charArray[l] == charArray[r]) {
queue.poll();
l++;
} else {
// 如果不是就一直将l右移并将l位置的字符移出set直到不再重复
do {
queue.poll();
l++;
} while (charArray[l] != charArray[r]);
}
}
}
return longestLength;
}
}
方案二:滑动窗口的优化
自己想的方案中使用了额外的数据结构来进行滑动窗口中数据的存放和重复的判断;可以使用一个int[]数组来实现。
提示中说s由英文字母、数字、符号和空格组成,这些字符显然都在ASCII表中。所以定义一个int[128]存放每一个字符上一次出现的index,如果出现下一次就覆盖。
class Solution {
public int lengthOfLongestSubstring(String s) {
// base case(边界条件)
if(s == null) {
return 0;
}
char[] charArray = s.toCharArray();
if(charArray.length == 0 || charArray.length == 1) {
return charArray.length;
}
// 定义一个可以存放ASCII表中所有字符的int[]数组并初始化(-1表示还没出现过)
int[] last = new int[128]; // index是字符的ascii码值,值表示是否出现
for(int i = 0; i < 128; i++) {
last[i] = -1;
}
int res = 0;
int start = 0; // 窗口开始位置
// 然后开始遍历charArray
for (int i = 0; i < charArray.length; i++) {
// 取得当前字符的ascii码值
int index = charArray[i];
// 比较滑动窗口开始位置和当前字符上一次出现位置的下一位
// 其实就是把滑动窗口的开始位置移到当前字符上一次出现位置的下一位(这样做才能避免出现重复)
// 如果当前字符没有出现过,last[index]的值为-1,+1后等于0
start = Math.max(start, last[index]+1);
// 比较是否出现了新的最长子串
res = Math.max(res, i-start+1);
// 不管之前以后没有出现过,都把当前字符的出现位置加进去
last[index] = i;
}
return res;
}
}