给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
又是一道中等难度的题,解法也十分巧妙,首先将字符串的存储转为ASCII码的存储,这样做的意义就在于能够将循环查找改为索引查找,在判断该字符是否为首次出现有很大帮助。其次这样可以只使用一次循环,在遇到相同字符时从前一字符的下一位开始继续寻找更长的子串,方便快捷。
public int lengthOfLongestSubstring(String s) {
int[] last = new int[128];//定义数组范围
int n = s.length();//字符串长度
int start = 0; //起始位置
int res = 0;//所求子串最大长度
for (int i = 0; i < n; i++) {
//字符ASCII码值
int index = s.charAt(i);
//在字符出现后会给数组对应位置赋值,如果再次碰到该字符就将起始位置挪到该字符处继续向后查找
start = Math.max(start, last[index]);
//未遇到相同字符时start不变,res每次+1,遇到相同字符后前一段子串已经达到最大而后一段长度还能继续增长,一旦超过之前的最大值就取而代之
res = Math.max(res, i-start+1);
//i起始为0所以赋值从1开始
last[index] = i+1;
}
return res;
}
此外,此解与官方解有区别的地方就在于这里在遇到重复字符时采取的方式为将新的子串起始位置变为第一次重复的字符右一位,而不是按顺序每次都向右移一位,这样做的好处为减少循环次数,但缺点为增加了空间去记录每个字符的位置,属于以空间换时间的做法,两者皆可。
官方解思路如下:
···
这道题主要用到思路是:滑动窗口
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!
如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
时间复杂度:O(n)
也可以用HashMap求解滑动窗口。
public int lengthOfLongestSubstring(String s) {
if (s.length()==0) return 0;
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
int max = 0;
int left = 0;
for(int i = 0; i < s.length(); i ++){
if(map.containsKey(s.charAt(i))){
left = Math.max(left,map.get(s.charAt(i)) + 1);
}
map.put(s.charAt(i),i);
max = Math.max(max,i-left+1);
}
return max;
}