给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串的长度。
输入:字符串
输出:整型变量
思路:两个指针一前一后,最开始都在0位置,然后有值则快指针向右移动一位,判断是否存在过
class Solution {
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
Map<Character, Integer> map = new HashMap<>();
for (int end = 0, start = 0; end < n; end++) {
char alpha = s.charAt(end);
if (map.containsKey(alpha)) {
start = Math.max(map.get(alpha), start);
}
ans = Math.max(ans, end - start + 1);
map.put(s.charAt(end), end + 1);
}
return ans;
}
}
二刷感悟:
以 (a)bcabcbb 开始的最长字符串为 (abc)abcbb;
以 a(b)cabcbb 开始的最长字符串为 a(bca)bcbb;
以 ab(c)abcbb 开始的最长字符串为 ab(cab)cbb;
以 abc(a)bcbb 开始的最长字符串为 abc(abc)bb;
以 abca(b)cbb 开始的最长字符串为 abca(bc)bb;
以 abcab(c)bb 开始的最长字符串为 abcab(cb)b;
以 abcabc(b)b 开始的最长字符串为 abcabc(b)b;
以 abcabcb(b) 开始的最长字符串为 abcabcb(b)。
关于滑动窗口,什么时候移动?怎么移动?为什么可以这么移动?
什么时候移动?
当判断某一个子串中有重复的字符时,开始移动,
怎么移动?
如果当前字符的加入,导致子串重复了,那么子串的头取掉一个,继续判断是否加入的当前字符导致新子串重复
为什么可以这么移动?
因为根据上面的推理,以abcabcbb为例,依次递增子串的起始位置,结束位置也在递增,并且当一个子串的起始位置是k,结束位置是r,那么当起始位置为k+1的子串,从k+1一直到r的子串一定是无重复字符的
具体方法:
使用HashSet存储子串的每一个字符,当左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往HashSet中添加一个元素。
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
Set<Character> set = new HashSet<>();
int r = -1;
int max = 0;
for(int i = 0; i < len; i++){
if(i != 0){
set.remove(s.charAt(i - 1));
}
while(r + 1 < len && !set.contains(s.charAt(r + 1))){
set.add(s.charAt(r + 1));
r++;
}
max = Math.max(max, r - i + 1);
}
return max;
}
}
每次都要移动起始位置,所以使用for遍历
使用哈希表可以每次都移动结束位置,这样就只需判断起始位置从什么时候开始就可以,也就是这一个 l = Math.max(map.get(s.charAt(i)) + 1, l);的关键
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> map = new HashMap<>();
int l = 0;
int num = 0;
for(int i = 0; i < s.length(); i++){
if(map.containsKey(s.charAt(i))){
// l = Math.max(map.get(s.charAt(i)) + 1, l);
while(l < map.get(s.charAt(i)) + 1){
map.remove(l);
l++;
}
}
map.put(s.charAt(i), i);
num = Math.max(num, i - l + 1);
}
return num;
}
}
注意,如果每次都移动起始位置,则使用HashSet比较合适,因为不用知道具体的索引位置,每次把这个字符移除掉就行,然后再进行右边字符的添加
但是!!!对于每次都移动结束位置,每次遍历都需要将结束位置加入集合中,此时必须使用HashMap,因为如果当前元素加入之后,在原本的HashMap表中已经存在,则可以根据索引将起始位置移动到不发生重复的那一位,然后根据索引计算子串中的元素数量,此题得解!
思考:那么如果每次移动起始位置,可以使用HashMap吗???
yes,也是可以,但是非常慢
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
Map<Integer,Character > map = new HashMap<>();
int r = -1;
int ans = 0;
for(int i = 0; i < len; i++){
if(i != 0){
map.remove(i - 1);
}
while(r + 1 < len && !map.containsValue(s.charAt(r + 1))){
map.put(r + 1, s.charAt(r + 1));
r++;
}
ans = Math.max(ans, r + 1 - i);
}
return ans;
}
}

可以将HashMap换成数组,操作起来比较方便,也更快
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
int[] charIndex = new int[128]; // ASCII 字符范围
Arrays.fill(charIndex, -1); // 初始化为 -1,表示字符未出现过
int r = -1;
int ans = 0;
for (int i = 0; i < len; i++) {
if (i != 0) {
charIndex[s.charAt(i - 1)] = -1; // 移除左边界字符
}
while (r + 1 < len && charIndex[s.charAt(r + 1)] == -1) {
charIndex[s.charAt(r + 1)] = r + 1; // 记录字符索引
r++;
}
ans = Math.max(ans, r + 1 - i);
}
return ans;
}
}
用-1来标记当前字符是否出现过
599

被折叠的 条评论
为什么被折叠?



